I'm trying to create a service that will be a middle man between my frontend framework and shutterstock. I'm running into an issue when trying to license an image where it says my subscription is unusable or cannot be found. I have done exactly what the documentation said and I don't know what I am missing.
let sstk = require("shutterstock-api");
sstk.setSandbox(true);
sstk.setAccessToken(process.env.SHUTTERSTOCK_TOKEN);
// Instantiate the shutterstock images api
const imagesApi = new sstk.ImagesApi();
// Instantiate the shutterstock users api
const usersApi = new sstk.UsersApi();
// Creates the body to send to shutterstock
const body = {
images: imageIds.map((imageId) => {
return {
image_id: imageId,
price: 0,
metadata: {
customer_id: "0",
},
};
}),
};
// Get subscription so we can grab the subscription id
usersApi
.getUserSubsciptionList()
.then(({ data }) => {
const subscription_id = data[0].id;
const queryParams = {
format: "jpg",
size: "huge",
subscription_id,
};
// If we successfully get the subscription id then license the images
imagesApi
.licenseImages(body, queryParams)
.then(({ data }) => {
console.log("licensedImages", data);
// Check if there was an error on any of the images
let numOfErrors = 0;
data.forEach((image) => {
if (image.error) {
numOfErrors += 1;
}
});
// If some of the images were successful
if (numOfErrors > 0 && numOfErrors < data.length) {
return errorHandler
// If all the images failed
} else if (numOfErrors > 0) {
return errorHandler
}
// If there are no errors send back the data to the frontend to manipulate it how it needs
return res.status(200).send(data);
})
.catch((err) => {
// If license error wasn't handled by Shutterstock
console.error(err);
return errorHandler
});
})
.catch((error) => {
// If subscription error wasn't handled by Shutterstock
console.error(error);
return errorHandler
});
Logged Response with status code 200
licensedImages [ exports {
image_id: id,
error:
'ValidationError: Subscription is unusable or cannot be found. License failed' } ]
I'm not sure why its not working. I've logged my subscription id and image id and they are correct.
The format and size do match the formats available on the subscription.
The subscription is a Developer Platform license.
What am I missing?
This is on an expressjs api
It looks like your Shutterstock account has both a 'Developer Platform' subscription and standard user subscription, which causes issues in the api. Your code is correct - the problem is with the validation of your subscription within the licensing flow. We'll reach out to you via email once we correctly attribute your different subscriptions.
Related
I've implemented the agora sdk 3.0 for video calls.
now I'm trying to get screen sharing to work, but I keep getting the error provided in
the picture below (Join failed: NO_AUTHORIZED).
Picture of console while sharing a screen
screen sharing code sample:
async shareScreen() {
this.shareClient = AgoraRTC.createClient({
mode: 'rtc',
codec: 'vp8'
})
this.shareClient.init('xxxxxxxxxxxxxx', () => {
this.shareClient.join('same token video call started with', 'same room name of current outgoing video call', null, (uid) => {
const streamSpec = {
streamID: uid,
audio: false,
video: false,
screen: true
}
if (isFirefox()) {
streamSpec.mediaSource = 'window';
} else if (!isCompatibleChrome()) {
streamSpec.extensionId = 'minllpmhdgpndnkomcoccfekfegnlikg';
}
this.shareScreenStream = AgoraRTC.createStream(streamSpec);
// Initialize the stream.
this.shareScreenStream.init(() => {
// Play the stream.
this.shareScreenStream.play('renderer');
// Publish the stream.
this.shareClient.publish(this.shareScreenStream);
}, function(err) {
console.log(err);
});
}, function(err) {
console.log(err);
})
});
},
The screensharing client should use an unique token based on the UID and channel name. Not the one the main user is using.
Stripe webhook fails and i assume its because Stripe doesnt recieve 'success' response from my webhook api endpoint.
Error is:
Test webhook error: 504
An error occurred with your deployment
FUNCTION_INVOCATION_TIMEOUT
I use Nextjs and its build in pages/api/createOrder folder structure to create a api. This is how my createOrder webhook looks like:
import { buffer } from "micro"
const AWS = require("aws-sdk")
AWS.config.update({
accessKeyId: process.env.MY_AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.MY_AWS_SECRET_ACCESS_KEY,
region: process.env.MY_AWS_REGION,
endpoint: process.env.MY_AWS_ENDPOINT,
})
// Establish Stripe connection
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY)
const endpointSecret = process.env.STRIPE_CREATE_ORDER_SIGNING_SECRET
const createOrder = async session => {
console.log("create order - session.id: ", session.id)
console.log(
"create order - session.metadata.userID: ",
session.metadata.userID
)
console.log(
"create order - session.amount_total / 100: ",
session.amount_total / 100
)
const docClient = new AWS.DynamoDB.DocumentClient()
let date = new Date()
// create Order
let orderParams = {
TableName: process.env.ORDER_TABLE_NAME,
Item: {
id: session.id,
userID: session.metadata.userID,
amount: session.amount_total / 100,
adID: session.metadata.adID,
createdAt: date.toISOString(),
updatedAt: date.toISOString(),
},
}
docClient.put(orderParams, function (err, data) {
if (err) {
console.log("Order put err - " + JSON.stringify(err, null, 2))
} else {
console.log("Order put Success - " + JSON.stringify(data, null, 2))
}
})
// create - TTL
const ninetyDays = 1000 * 60 * 60 * 24 * 90
const currTime = Date.now()
const ttlSeconds = Math.ceil((ninetyDays + currTime) / 1000)
// update Ad
let adParams = {
TableName: process.env.AD_TABLE_NAME,
Key: { id: session.metadata.adID },
UpdateExpression: "set paid = :paid, expdate = :expdate",
ExpressionAttributeValues: {
":paid": true,
":expdate": ttlSeconds,
},
ReturnValues: "UPDATED_NEW",
}
docClient.update(adParams, function (err, data) {
if (err) {
console.log("UPDATE Ad err - " + JSON.stringify(err, null, 2))
} else {
console.log("UPDATE Ad Success - " + JSON.stringify(data, null, 2))
}
})
}
export default async (req, res) => {
if (req.method === "POST") {
const requestBuffer = await buffer(req)
const payload = requestBuffer.toString()
const sig = req.headers["stripe-signature"]
let event
// Verify that the EVENT posted came from stripe
try {
event = stripe.webhooks.constructEvent(payload, sig, endpointSecret)
} catch (err) {
console.log("ERROR", err.message)
return res.status(400).send(`Webhook create Order error: ${err.message}`)
}
// Handle the checkout.session.completed event
if (event.type === "checkout.session.completed") {
const session = event.data.object
// Fulfill update Ad -> paid = true and ttl - expdate
return createOrder(session)
.then(() => res.status(200))
.catch(err =>
res.status(400).send(`Create Order Error - ${err.message}`)
)
}
}
// Notify Stripe that req reached api
res.status(200).json({ received: true })
}
export const config = {
api: {
bodyParser: false,
externalResolver: true,
},
}
I found few solutions how to tell Stripe that the webhook call was recieved but none of them worked. Right now im useing:
res.status(200).json({ received: true })
Maybe the problem is located somewhere else?
I want to note that Order is created and Ad is updated - so the webhook works as expected except that it fails.
In an event-driven architecture (webhooks), you usually want to return a 200 as fast as possible to the provider and then process the event after. This requires a queue to decouple.
You can check out https://hookdeck.com/. Their product seems to do exactly that (returning 200 to Stripe under 200ms) so you don't get Timeout errors.
It looks like the FUNCTION_INVOCATION_TIMEOUT error is specific to Vercel, so is your app hosted there? Apparently the timeouts are quite low, and you could try increasing them.
Typically what you'd want to do when receiving a webhook is:
Store the webhook (e.g. in the database)
Send your HTTP response
Actually handle the webhook (e.g. in a background job)
This makes the response time faster and might solve your problem. It sounds like your setTimeout solution is kind of doing this — it's maybe sending the response before the handler is finished, which will be faster than putting it at the end of the handler.
After 2 month of debugging i found a solution.
I think the problem was that AWS seems to be SLOW. I added setTimeout for notifying Stripe that connection was successfull and it now works!
setTimeout(() => {
// 3. Notify Stripe that event recieved.
res.json({ received: true })
}, 2000)
If some1 has better solution plz tell us :)
I’m working on a Netlify Function where we take form data for a job application (including a file upload) and pass the data on to a third-party API for use in their system. I was following along with this handy post (thanks!) —
https://www.netlify.com/blog/2021/07/29/how-to-process-multipart-form-data-with-a-netlify-function/
— but seem to have run into a situation where the data in the file is not handled properly (for example, PDFs turn up with blank content, though ASCII metadata appears to be at least partly intact), at least when using the Netlify CLI; I have yet to try on a deploy preview. Writing to a local directory confirms that the issue isn’t with the third party API. Is there something I’m missing when working with these files? Example code below (note that I’ve also attempted to work with the Buffer data, with identical results).
Fetch function to call the Netlify Function:
const data = new FormData(form);
fetch('/.netlify/functions/apply', {
method: 'POST',
body: data,
}).then(res => {
if (!res.ok && res.status !== 406) {
throw new Error('oh no');
}
return res.json();
}).then(data => {
if (Array.isArray(data.missingRequiredFields) && data.missingRequiredFields.length > 0) {
console.log(data);
showMissingFields(data.missingRequiredFields);
} else {
showConfirmationMessage(data.message);
}
}).catch(err => {
showWarningMessage('Something went wrong; please try again.');
}).finally(() => {
submitButton.removeAttribute('disabled');
});
And here’s our Netlify Function:
const Busboy = require("busboy");
const FormData = require("form-data");
const fetch = require("node-fetch");
function parseMultipartForm(event) {
// source: https://www.netlify.com/blog/2021/07/29/how-to-process-multipart-form-data-with-a-netlify-function/
return new Promise(resolve => {
const fields = {};
const busboy = new Busboy({
// uses request headers to extract the form boundary value
headers: event.headers,
});
// before parsing anything, we need to set up some handlers.
// whenever busboy comes across a file ...
const f = [];
busboy.on("file", (fieldname, filestream, filename, transferEncoding, mimeType) => {
// ... we take a look at the file's data ...
filestream.on("data", data => {
fields[fieldname] = {
filename,
type: mimeType,
content: data,
transferEncoding,
};
});
});
// whenever busboy comes across a normal field ...
busboy.on("field", (fieldName, value) => {
// ... we write its value into `fields`.
fields[fieldName] = value;
});
// once busboy is finished, we resolve the promise with the resulted fields.
busboy.on("finish", () => {
resolve(fields);
});
// now that all handlers are set up, we can finally start processing our request!
busboy.write(event.body);
});
}
/** ***************************************************************************
* Serverless function
**************************************************************************** */
exports.handler = async function(event, context) {
// parse the incoming multipart/form-data data into fields object
const fields = await parseMultipartForm(event);
// create new formdata object to be send to Lever
const form = new FormData();
for (const [key, value] of Object.entries(fields)) {
if (key === "resume") {
// append "resume" with the file buffer and add the file name
form.append("resume", value.content, { filename: value.filename });
} else {
form.append(key, value);
}
}
};
Any guidance you might have would be greatly appreciated.
API
(https://api.mosmarts.com/truck/v0/api.php)
The API is scripted in PHP and accepts GET & POST commands and in return it responds back with a JSON response data.
To retrieve data the API requires “functionality” and “action” among other params as show below.
Command for retrieving all truck
Command for retrieving all truck
Payloads
{
"functionality" : "get",
"action" : "get_all_truck"
}
Command to retrieving truck inspection details by id
Payloads
{
"functionality" : "get",
"action" : "get_inspection_history",
"truckId" : "1"
}
NB: you will get truckId from command "get_all_truck" above
What’s expected from you
As the software developer you are tasked to design and develop a web-based backend solution that will have:
Dashboard: -
• Retrieve and indicate total number of trucks
• Retrieve and indicate number of inspection repairs requested 2. List all Trucks: -
• Implement search option
Inspection List: -
• Implement filter by truck
i have some code using express.js bt i get is a 404 error, no data retrieved.
app.js
const apiCallFromRequest = require('./Request')
const apiCallFromNode = require('./NodeJsCall')
const http = require('http')
http.createServer((req, res) => {
if(req.url === "/request"){
apiCallFromRequest.callApi(function(response){
//console.log(JSON.stringify(response));
res.write(JSON.stringify(response));
res.end();
});
}
else if(req.url === "/node"){
apiCallFromNode.callApi(function(response){
res.write(response);
res.end();
});
}
// res.end();
}).listen(3000);
console.log("service running on 3000 port....");
NodeJsCall.js
const https = require('https');
_EXTERNAL_URL = 'https://api.mosmarts.com/truck/v0/api.php';
const callExternalApiUsingHttp = (callback) => {
https.get(_EXTERNAL_URL, (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
return callback(data);
// console.log(JSON.stringify(data));
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
}
module.exports.callApi = callExternalApiUsingHttp;
Request.js
const request = require('request');
_EXTERNAL_URL = 'https://api.mosmarts.com/truck/v0/api.php';
const callExternalApiUsingRequest = (callback) => {
request(_EXTERNAL_URL, { json: true }, (err, res, body) => {
if (err) {
return callback(err);
}
return callback(body);
});
}
module.exports.callApi = callExternalApiUsingRequest;
Hey Gerald you can find a simple response for this kind of question on google.
if you are a real beginner I would propose you the Axios npm.
here is an example of a really simple GET request with axios.
axios.get('https://api.github.com/users/mapbox')
.then(response => {
console.log(response.data.created_at);
});
When I want to retrieve ParseObjects (Posts) from parse db (mongo) to display in my Android app, I need to add new fields to ParseObject in the cloud code before delivering to client. these fields are only necessary for the client and thus I do not want to save them to the cloud/db. but for some weird reason it seems like the additional fields are only delivered to client successfully if I save them to cloud.
something like this will work:
Parse.Cloud.define("getPosts", function(request, response){
const query = new Parse.Query("Post");
query.find()
.then((results) => {
results.forEach(result => {
result.set("cloudTestField", "this is a testing server cloud field");
});
return Parse.Object.saveAll(results);
})
.then((results) => {
response.success(results);
})
.catch(() => {
response.error("wasnt able to retrieve post parse objs");
});
});
this delivers all new fields to my client.
but if I don't save them to db and just add them prior to client delivery
something like this:
Parse.Cloud.define("getPosts", function(request, response){
const query = new Parse.Query("Post");
query.find()
.then((results) => {
results.forEach(result => {
result.set("cloudTestField", "this is a testing server cloud field");
});
response.success(results);
})
.catch(() => {
response.error("wasnt able to retrieve post parse objs");
});
});
Then for some reason, In my android studio (client log), I receive null on the key "cloudTestField"
ParseCloud.callFunctionInBackground("getPosts", params,
new FunctionCallback<List<ParseObject>>(){
#Override
public void done(List<ParseObject> objects, ParseException e) {
if (objects.size() > 0 && e == null) {
for (ParseObject postObj : objects) {
Log.d("newField", postObj.getString("cloudTestField"));
}
} else if (objects.size() <= 0) {
Log.d("parseCloudResponse", "sorry man. no objects from server");
} else {
Log.d("parseCloudResponse", e.getMessage());
}
}
});
and for some reason, the output is:
newField: null
How do I add fields to ParseObjects in cloud without saving to db
Turnes out, you cannot add fields whom are not persistent - to ParseObject.
So I needed to convert the parseObjects to Json and now it's working like a charm:
Parse.Cloud.define("getPosts", function(request, response){
const query = new Parse.Query("Post");
var postJsonList = [];
query.find()
.then((results) => {
results.forEach(result => {
var post = result.toJSON();
post.cloudTestField = "this is a testing server cloud field";
postJsonList.push(post);
});
response.success(postJsonList);
})
.catch(() => {
response.error("wasnt able to retrieve post parse objs");
});
});