Braintree javascript Drop-in 3d Secure 2 returning 2099 - braintree

3d secure checks seem to sometimes return a 2099 - which suggests i'm not doing a client-side check.
Can someone let me know what i'm missing?
form.addEventListener('submit', function (event) {
event.preventDefault();
dropinInstance.requestPaymentMethod({threeDSecure: threeDSecureParameters}, function (err, payload) {
if (err) {
console.log('error in request payment - ', err);
return;
}
if (payload.type !== 'CreditCard') {
nonceInput.value = payload.nonce;
form.submit();
} else {
let liabilityShifted = payload.liabilityShifted
if (liabilityShifted !== true){
dropinInstance.clearSelectedPaymentMethod();
console.log('card payment was unsuccessful')
alert('Payment unsuccessful, please use another card, or alternately use PayPal');
} else {
nonceInput.value = payload.nonce;
form.submit();
}
}
});
});
the response comes back with this snippet:
{
"nonce": "c47ba6c8-2b01-05f0-22e1-cec40f25c734",
"details": {
"cardholderName": null,
"expirationMonth": "10",
"expirationYear": "2023",
"bin": "xxxxxx",
"cardType": "Visa",
"lastFour": "xxx",
"lastTwo": "xx"
},
"type": "CreditCard",
"description": "ending in 67",
"liabilityShifted": true,
"liabilityShiftPossible": true,
"threeDSecureInfo": {
"liabilityShifted": true,
"liabilityShiftPossible": true,
"status": "authenticate_successful",
"enrolled": "Y",
which suggests authorisation was approved (the customer sees a zero € transaction - but when i then try and create a subscription it rejects it with:
processorResponseCode: '2099',
processorResponseText: 'Authentication Required',
what have i missed?

Related

Slack Bolt App: Options body view state is not updated like actions body view state

I am trying to implement dependent external selects inside a modal but I am having problems passing the state of the first dropdown to the second. I can see the state I need inside the app.action listener but I am not getting the same state inside the app.options listener.
body.view.state inside app.action("case_types"). I specifically need the case_create_case_type_block state.
"state": {
"values": {
"case_create_user_select_block": {
"case_create_selected_user": {
"type": "users_select",
"selected_user": "U01R3AE65GE"
}
},
"case_create_case_type_block": {
"case_types": {
"type": "external_select",
"selected_option": {
"text": { "type": "plain_text", "text": "Incident", "emoji": true },
"value": "Incident"
}
}
},
"case_create_case_subtype_block": {
"case_subtypes": { "type": "external_select", "selected_option": null }
},
"case_create_case_owner_block": {
"case_owners": { "type": "external_select", "selected_option": null }
},
"case_create_subject_block": {
"case_create_case_subject": {
"type": "plain_text_input",
"value": null
}
},
"case_create_description_block": {
"case_create_case_description": {
"type": "plain_text_input",
"value": null
}
}
}
},
body.view.state inside app.options("case_subtypes")
"state": {
"values": {
"case_create_user_select_block": {
"case_create_selected_user": {
"type": "users_select",
"selected_user": "U01R3AE65GE"
}
}
}
},
I did also try to update the view myself hoping it would update the state variables inside app.action({ action_id: "case_types" })
//need to update view with new values
try {
// Call views.update with the built-in client
const result = await client.views.update({
// Pass the view_id
view_id: body.view.id,
// Pass the current hash to avoid race conditions
hash: body.view.hash,
});
console.log("Case Type View Update result:");
console.log(JSON.stringify(result));
//await ack();
} catch (error) {
console.error(error);
//await ack();
}
I ended up posting this on the github issues page for slack bolt. This was a bug that will fixed in a future release. Below is the workaround using private metadata to hold the state to check for future dependent dropdowns.
// Action handler for case type
app.action('case_type_action_id', async ({ ack, body, client }) => {
try {
// Create a copy of the modal view template and update the private metadata
// with the selected case type from the first external select
const viewTemplate = JSON.parse(JSON.stringify(modalViewTemplate))
viewTemplate.private_metadata = JSON.stringify({
case_type: body.view.state.values['case_type_block_id']['case_type_action_id'].selected_option.value,
});
// Call views.update with the built-in client
const result = await client.views.update({
// Pass the view_id
view_id: body.view.id,
// Pass the current hash to avoid race conditions
hash: body.view.hash,
// Pass the updated view
view: viewTemplate,
});
console.log(JSON.stringify(result, 0, 2));
} catch (error) {
console.error(error);
}
await ack();
});
// Options handler for case subtype
app.options('case_subtype_action_id', async ({ body, options, ack }) => {
try {
// Get the private metadata that stores the selected case type
const privateMetadata = JSON.parse(body.view.private_metadata);
// Continue to render the case subtype options based on the case type
// ...
} catch (error) {
console.error(error);
}
});
See the full explaination here: https://github.com/slackapi/bolt-js/issues/1146

Remove null results from a array that can contain nullable values in GraphQL

I have a query in my app that works but response is little ugly, there is probably two ways to solve this:
Write resolver differently
Clean response from null values
Here is resolver:
t.list.field('manyDevices', {
type: 'Device',
description: 'Get list of devices belonging to user',
args: {
input: nonNull(deviceIdentifierInput.asArg()),
},
resolve: async (_, { input: { id } }, { prisma }) => {
return await prisma.device.findMany({ where: { userId: id } });
},
});
This resolver looks for all devices with provided id. Id can be mine and also can be from a some other user. Devices can be public and private, and I don't want to receive private devices except if they are mine.
const isDevicePublic = rule({ cache: 'strict' })(
async ({ isPublic }: Device) => {
if (!isPublic) {
return permissionErrors.noPermission;
}
return true;
},
);
const isDeviceOwner = rule({ cache: 'strict' })(
async ({ userId }: Device, _, { user }: Context) => {
assertValue(user, permissionErrors.noAuthentication);
if (userId !== user.id) {
return permissionErrors.noPermission;
}
return true;
},
);
These are rules that I place on my schema with graphql-shield library and it works. There is just one problem, if a user have a private device it will be listed in response array as null and graphql-shield will throw error, so response can look like this:
{
"errors": [
{
"message": "You have no permission to access this resource",
"locations": [
{
"line": 3,
"column": 5
}
],
"path": [
"manyDevices",
0,
"name"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"Error: You have no permission to access this resource",
" at Rule.resolve (/workspace/node_modules/graphql-shield/dist/rules.js:33:24)",
" at runMicrotasks (<anonymous>)",
" at processTicksAndRejections (internal/process/task_queues.js:93:5)",
" at async Promise.all (index 0)"
]
}
}
}
],
"data": {
"manyDevices": [
null,
{
"name": "device-01"
}
]
}
}
So there is one fetched device and other that is private that throws this error, can I somehow remove null and error response or should I filter them out in resolver?

Loopback : Validate model from another model is not returning proper error message

I am validating model from another model like below
Model.addFavorite = function (data, callbackFn) {
if (data) {
var faviroteModel = this.app.models.Favorite;
var objFavorite = new faviroteModel(data);
objFavorite.isValid(function (isValid) {
if (isValid) {
callbackFn(null, objFavorite);
}
else {
callbackFn(objFavorite.errors);
}
});
}
else callbackFn("Post data required", {});
}
If i do this then I am getting error like below
{
"error": {
"statusCode": 500,
"t": [
"is not a valid date"
]
}
}
It should be with error message like below
{
"error": {
"statusCode": 422,
"name": "ValidationError",
"message": "The `Favorite` instance is not valid. Details: `t` is not a valid date (value: Invalid Date).",
"details": {
"context": "Favorite",
"codes": {
"t": [
"date"
]
},
"messages": {
"t": [
"is not a valid date"
]
}
}
}
}
Can anyone tell me what am i missing here.
How can i achieve this.
https://github.com/strongloop/loopback-datasource-juggler/blob/master/lib/validations.js#L843
You might run into situations where you need to raise a validation
error yourself, for example in a "before" hook or a custom model
method.
if (model.isValid()) {
return callback(null, { success: true });
}
// This line shows how to create a ValidationError
var err = new MyModel.ValidationError(model);
callback(err);
}

How to use the post_poll method in Zapier CLI

According to the docs, I should use a post_poll function to add the missing id field in the response.
How do I add the post_poll function ?
Here's my error:
Results must be an array, got: object,
({"totalevents":83,"events":[{"eventid":10266033,"c)
- Got a result missing the "id" property (83)
Tried following this but it is not clear to me, I'm very new to Zapier-CLI
Update - adding code
This is the function that returns the data:
const listEvents = (z, bundle) => {
console.log('listing events.. ');
let client_id = bundle.inputData.client_id;
const requestOpts = {
url: `https://wccqa.on24.com/wcc/api/v2/client/${client_id}/event`
};
return z.request(requestOpts)
.then((response) => {
return z.JSON.parse(response.content);
});
};
The sample response is the following, with the distiction that I added the id param manually to avoid errors when zapier test|push:
{
"id": 9964513,
"eventid": 9964513,
"archivestart": "2017-09-21T10:30:00-07:00",
"archiveend": "2018-09-21T10:30:00-07:00",
"description": "Zapier Event Test",
"iseliteexpired": "N",
"displaytimezonecd": "America/Bogota",
"eventtype": "Live Webcam ",
"regrequired": true,
"clientid": 22921,
"liveend": "2017-09-21T10:00:00-07:00",
"createtimestamp": "2017-09-21T09:47:44-07:00",
"audienceurl": "https://localhost.on24.com/wcc/r/9964513/C49755A02229BD48E6010848D7C81EF8",
"lastmodified": "2017-09-21T09:47:44-07:00",
"livestart": "2017-09-21T08:45:00-07:00",
"goodafter": "2017-09-21T09:00:00-07:00",
"regnotificationrequired": true,
"isactive": true,
"localelanguagecd": "en"
}
The ACTUAL response from the endpoint the following which is used in the app created in the Web Builder App instead of CLI and works fine:
{
"events": [
{
"eventid": 9964513,
"archivestart": "2017-09-21T10:30:00-07:00",
"archiveend": "2018-09-21T10:30:00-07:00",
"description": "Zapier Event Test",
"iseliteexpired": "N",
"displaytimezonecd": "America/Bogota",
"eventtype": "Live Webcam ",
"regrequired": true,
"clientid": 22921,
"liveend": "2017-09-21T10:00:00-07:00",
"createtimestamp": "2017-09-21T09:47:44-07:00",
"audienceurl": "https://localhost.on24.com/wcc/r/9964513/C49755A02229BD48E6010848D7C81EF8",
"lastmodified": "2017-09-21T09:47:44-07:00",
"livestart": "2017-09-21T08:45:00-07:00",
"goodafter": "2017-09-21T09:00:00-07:00",
"regnotificationrequired": true,
"isactive": true,
"localelanguagecd": "en"
}
],
"totalevents": 1
}
I was thinking something along the line of the following, but how do I register this ?
const postPoll = (event,z,bundle) => {
if(event.key === 'events'){
var results = z.JSON.parse(bundle.request.data).results;
var events = results.events.map(function(event){
event.id = event.eventid;
return event;
});
bundle.request.data = events;
}
};
module.exports = postPoll;
Nice, so you're almost there! CLI apps don't have pre_ and post_ poll methods. Instead, you put any manipulation after the response comes in.
const listEvents = (z, bundle) => {
console.log('listing events.. ');
let client_id = bundle.inputData.client_id;
const requestOpts = {
url: `https://wccqa.on24.com/wcc/api/v2/client/${client_id}/event`
};
return z.request(requestOpts)
.then((response) => {
return z.JSON.parse(response.content);
})
.then(data => {
const events = data.events; // array of events
return events.map(function(e){ // returns array of objects with `id` defined
e.id = e.event_id
return e
})
})
};

Lambda and API gateway mapping

I want to return a value from handler to API gateway response header.
Handler.js
module.exports.handler = function(event, context, cb) {
const UpdateDate = new Date();
return cb(null, {
body: {
message: 'test'
},
header: {
Last-Modified: UpdateDate
}
});
};
s-function.json in "endpoints"
"responses": {
"400": {
"statusCode": "400"
},
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Cache-Control": "'public, max-age=86400'",
"method.response.header.Last-Modified": "integration.response.body.header.Last-Modified"
},
"responseModels": {
"application/json;charset=UTF-8": "Empty"
},
"responseTemplates": {
"application/json;charset=UTF-8": "$input.json('$.body')"
}
}
}
This can work. But I want to know how to use "integration.response.header.Last-Modified". Is my handler callback formate wrong?
Edit:
s-function.json in "endpoints"
"integration.response.header.Last-Modified" This doesn't work.
I want to know specific handler return formate to pass data to "integration.response.header.Last-Modified".
"responses": {
"400": {
"statusCode": "400"
},
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Cache-Control": "'public, max-age=86400'",
"method.response.header.Last-Modified": "integration.response.header.Last-Modified"
},
"responseModels": {
"application/json;charset=UTF-8": "Empty"
},
"responseTemplates": {
"application/json;charset=UTF-8": "$input.json('$.body')"
}
}
}
All of the output from your lambda function is returned in the response body, so you will need to map part of the response body to your API response header.
module.exports.handler = function(event, context, cb) {
const UpdateDate = new Date();
return cb(null, {
message: 'test',
Last-Modified: UpdateDate
});
};
will produce payload "{"message" : "test", "Last-Modified" : "..."}"
In this case you would use "integration.response.body.Last-Modified" as the mapping expression. As a side note, naming things "body" and "header" in your response body may make the mapping expressions confusing to read.
Thanks,
Ryan

Resources