Apple IAP Receipt Validation Node.js - javascript

I'm trying to use Node.js from here for my IAP Receipt Validation, but it always returns error on a server's log: "The data in the receipt-data property was malformed."
Can someone help me to properly send base64 string to Node.js and to decode it there as same base64 string for receipt validation? I have zero experience with javascript and been trying to make this simple code work for two days now, to no avail.
Here is my Swift code:
let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
let receiptString = receiptData.base64EncodedString(options: [])
var request = URLRequest(url: URL(string: "https://us-central1-calendizer-6a849.cloudfunctions.net/receiptValidation")!)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
// attach receipt data to a request
request.httpBody = receiptString.data(using: .utf8)
print("httpBody: \(request.httpBody?.base64EncodedString())")
Logs.log("✉️ Receipt Validation -> sending request ...")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
...
}
Here is my Node.js code (Firebase Cloud Functions):
const functions = require('firebase-functions');
var iap = require('in-app-purchase');
exports.receiptValidation = functions.https.onRequest((request, response) => {
var receipt_try1 = request.body
var receipt_try2 = request.body.toString('base64');
var receipt_try3 = JSON.stringify(receipt);
var receipt_try4 = new Buffer(request.body.toString(), 'base64')
console.log("receipt: " + receipt_try1)
iap.config({
applePassword: 'my shared key',
test: true
});
iap.setup(function (error) {
if (error) {
console.log("Setup error:" + error) // Failed to validate
}
iap.validate(iap.APPLE, receipt_try1, function (error, appleResponse) {
if (error)
{
console.log("Validation error:" + error) // Failed to validate
}
if (iap.isValidated(appleResponse)) {
console.log("successful validation" + appleResponse)
response.send(appleResponse)
}
});
});
});

did it using json:
let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
let receiptBase64String = receiptData.base64EncodedString(options: [])
//.replacingOccurrences(of: "+", with: "%2B")
// prepare json data
let json: [String: Any] = ["receipt_string": receiptBase64String]
let jsonData = try? JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
// create post request
var request = URLRequest(url: URL(string: "https://us-central1-calendizer-6a849.cloudfunctions.net/receiptValidation")!)
//request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = jsonData
and in Node.js simply:
var receipt = request.body.receipt_string

Related

How do I receive multiple JSON objects and use each one seperately?

I am trying to receive JSON objects via a websocket server. And I receive one JSON object every second that I want to be able to use each one separately as it gives a status on whether someone is active or not. However, whenever I try and store all the JSON's into a data structure only the final JSON I receive will be stored the others get deleted.
So my question is: is there a way to store JSON into a data structure whilst receiving it?
This is the JSON I receive looks something like this:
{"studentNum":"21127985","active":false,"currentScore":0}
here is the code for receiving the json from the server:
const WebSocket = require('ws');
// const serverAddress = "ws://127.0.0.1:5000";
const serverAddress = ' *SERVER NAME*';
const ws = new WebSocket(serverAddress, {
headers: {
"user-agent": "Mozilla"
}
});
ws.on('open', function () {
});
ws.on('message', function (msg) {
fs = require('fs')
console.log(msg.toString())
var obj = JSON.parse(msg);
/* convert buff to string
const json = JSON.parse(file.toString())
json.push(msg);*/
var writerStream = fs.createWriteStream('output.json')
writerStream.write(msg, 'UTF-8')
writerStream.end();
writerStream.on('finish', function () {
});
writerStream.on('error', function (err) {
console.log(err.stack);
});
});
you can put fs outside of your WS message callback
const WebSocket = require('ws');
fs = require('fs');
var writerStream = fs.createWriteStream('output.json')
const serverAddress = "ws://127.0.0.1:5000";
const serverAddress = ' *SERVER NAME*';
const ws = new WebSocket(serverAddress, {
headers: {
"user-agent": "Mozilla"
}
});
ws.on('open', function () {
});
ws.on('message', function (msg) {
writerStream.write(msg, 'UTF-8')
writerStream.on('error', function (err) {
console.log(err.stack);
});
});
Or alternatively you can write file in chunks i.e. store 10 messages in an array and write it using fs.appendFile function.

Unauthorised (401) error while implementing the HMAC authentication in NodeJs

I'm trying to implement an API with HAMC authentication in Nodejs. But always I am receiving an Unauthorised (401) error.
I have followed the below instruction for API implementation:
Authentication
Users will authenticate with the web service using HMAC authentication. The following steps are necessary to authenticate with the web service:
Set the Date header on the HTTP Request to the current UTC date/time expressed in RFC 1123 format (example: Mon, 12 Jan 2015 20:50:07 GMT)
Create a “representation” of the request by concatenating the following values into a single string (in this order, with no separators):
2.1 The request method (GET or POST)
2.2 The full request URI (all lowercase) including any query string parameters
2.2.1. Example: https://estimationapi.zirmed.com/1.0/estimate/1234
2.3 The value of the Date header in “yyyy-MM-ddTHH:mm:ss” format (UTC).
2.4 The CustID.
2.5 The Base64 encoded string of the content body (your estimate in the case of a POST).
Compute an HMAC signature by hashing the UTF-8 encoded “representation” with your UTF-8 encoded private WebCallEncryptionKey using HMACSHA256 as the hashing algorithm.
Convert the resultant hash to a Base64 string. This will be the HMAC signature.
Set the Authorization header on the request by concatenating the CustID and HMAC signature separated by a colon. Specify HMACas the authorization scheme.
5.1 Example of an HMAC Authorization header: HMAC 605:pLKb9P8w5e83BMdUH4+m/EeY7O3PsbV5A89KF7IYjnM=
Below is the code I'm using...
const crypto = require("crypto");
const moment = require('moment');
const http = require('http');
let _apiKey = "API_KEY"; // API KEY/HMAC KEY
let _estimateBaseUrl = "http://estimationapi.zirmed.com/1.0";
let _estimateTransaction = "/estimate";
let CustomerID = 'CustomerID';
let BaseAddress = new URL(_estimateBaseUrl);
const utcDate = new Date().toUTCString();
let requestDt = utcDate
let RequestContent = "DATA IN JSON"
getEstimate = async (req, res) => {
let signature = await createRequestAuth(_estimateTransaction, "POST", requestDt, CustomerID, RequestContent);
let response = await getEstimateID(signature);
res.send(response);
};
createRequestAuth = async (uriPath, method, requestDate, custId, body = "") => {
let fullUri = new URL(BaseAddress + uriPath);
let authHashString = await CreateAuthHashString(method, fullUri.toString().toLowerCase(), requestDate, custId, body);
let signature = crypto.createHmac('sha256', _apiKey).update(authHashString).digest("base64");
console.log("signature is === : ", signature)
return signature;
}
getEstimateID = (signature) => {
const data = new TextEncoder().encode( JSON.stringify(RequestContent) );
const options = {
hostname: 'estimationapi.zirmed.com',
port: '',
path: '/1.0/estimate',
method: 'POST',
Headers: {
Authorization: `HMAC ${CustomerID}:${signature}`,
Date: requestDt,
"Content-Type": `application/json`,
'Content-Length': data.length
}
}
console.log("options ------- ", options);
const req = http.request(options, res => {
console.log(`statusCode is: ${res.statusCode}`)
console.log(`res is: ${res}`)
res.on('data', d => { process.stdout.write(d) })
})
req.on('error', error => { console.error('error', error) })
req.write(data)
req.end()
}
CreateAuthHashString = (method, fullUri, requestDate, custId, body) => {
let uri = fullUri.toLowerCase();
let httpMethod = method;
let content = Buffer.from(utf8.encode(body)).toString('base64');
let authHashString = "".concat("", httpMethod,
uri,
moment(requestDate).format('yyyy-MM-DDTHH:mm:ss'),
custId,
content
);
console.log('authHashString: ', authHashString);
return authHashString;
}
module.exports = {
getEstimate
}
Not sure if I have done some silly mistake here but please let me know if anyone is aware of this issue.
2.3 The value of the Date header in “yyyy-MM-ddTHH:mm:ss” format (UTC)
When you use moment(requestDate).format('yyyy-MM-DDTHH:mm:ss'), the formatted value is in local timezone. To format it in UTC, you need to do it as moment(requestDate).utc().format('yyyy-MM-DDTHH:mm:ss').

How to prevent async - await freezing in javascript?

Good day I have a custom adonisjs command that pulls from an API.
async handle (args, options) {
// Status
// Open = 1979
// Get all jobs with open status.
const pullJobController = new PullJobsFromJobAdderController;
let token = await pullJobController.get_token();
if(token){
const jobs = await this._getOpenJobs('https://jobs/open-jobs', token , 1979);
}
}
async _getOpenJobs(url, accessToken, status) {
url = url + '?statusId=' + status
const headers = {
'Authorization': 'Bearer ' + accessToken
}
const options = {
method: 'GET',
url: url,
headers: headers
}
return (await rp(options).then(function (result) {
return {
status: true,
info: JSON.parse(result)
}
}).catch(function (error) {
return {
status: false
}
}));
} // _getOpenJobs()
PullJobsFromJobAdderController
async get_token()
{
// This works if directly returning the token.
// return "9ade34acxxa4265fxx4b5x6ss7fs61ez";
const settings = await this.settings();
const jobAdderObject = new this.JobAdder(settings.jobadder['client.id'], settings.jobadder['client.secret'])
const jobadderOauthObject = this.model('JobadderOauth');
const accessInfo = await jobadderOauthObject.jobdderLatestAccess();
let isAccessExpired = await this.checkAccessValidity(accessInfo.created_at);
let accessToken = accessInfo.access_token;
let apiEndpoint = accessInfo.api_endpoint;
if(isAccessExpired === true){
let refreshTokenInfo = await jobAdderObject.refrehToken(accessInfo.refresh_token)
if (refreshTokenInfo.status === true) {
let refreshTokenDetails = JSON.parse(refreshTokenInfo.info)
accessToken = refreshTokenDetails.access_token
apiEndpoint = refreshTokenDetails.api
await jobadderOauthObject.create({
code: accessInfo.code,
access_token: refreshTokenDetails.access_token,
refresh_token: refreshTokenDetails.refresh_token,
scope: 'read write offline_access',
api_endpoint: refreshTokenDetails.api
})
}
}
return accessToken;
} // get_token()
The function async get_token works as expected, it supplies me with a fresh token to be used by the adonisjs command. However it freezes after running the command.
But if I return the string token directly. The custom command handle() works as expected and terminates after running.
Scenario 1: (Directly returning the token string from PullJobsFromJobAdderController)
I run my custom command "adonis pull:jobs" and it runs as expected displaying in the terminal the result of the pulled data from the api.
Terminal is ready to accept another command.
Scenario 2: (Comment out the directly returned string token from PullJobsFromJobAdderController)
I run my custom command "adonis pull:jobs" and it runs as expected
displaying in the terminal the result of the pulled data from the
api.
Terminal is not accepting commands until I press ctrl+c and terminate the current job/command.
Perhaps I am missing something regarding async await calls.
Can someone point / help me to the right direction?
TIA
I got it, for anyone else having this kind of problem with adonis commands:
wrap the task inside your handle in a try... catch block then always have Database.close() and process.exit() in finally.

How to send queryStringParameters with invokeApi command

The full path to the endpoint with the query string parameters is:
https://api.mydomain.com/getData?param_01=value_01&param_02=value_01
After importing the 'aws-api-gateway-client'
var apigClientFactory = require('aws-api-gateway-client').default;
I go ahead and configure the variables:
let url = 'https://api.mydomain.com'
let pathTemplate = '/getData?param_01=value_01&param_02=value_01';
let method = 'GET';
let params = '';
let additionalParams = '';
let body = '';
var client = apigClientFactory.newClient({
invokeUrl: url,
accessKey: 'my-accessKeyId',
secretKey: 'my-secretAccessKey',
sessionToken: 'my-sessionToken',
region: 'MY_AWS_REGION'
});
Next invoke endpoint with:
client
.invokeApi(params, pathTemplate, method, additionalParams, body)
.then(function(res) {
console.log("...res:", res);
})
.catch(function(err) {
console.log("...err:", err);
});
But it fails with the error
The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details
Is there a way to send the queryStringParameters with invokeApi command?
let params = {};
let pathTemplate = '/getData';
let additionalParams = {
queryParams: {
param0: 'value0',
param1: 'value1'
}
};
aws-api-gateway-client - npm

Requesting Discord Attachments

What I'm trying to do:
I'm trying to scrape all images in a discord channel and getting their URL by requesting attachments but I can't seem to find a way to request it
Code
const fs = require("fs");
const fetch = require("node-fetch");
function readFileString(path) {
return fs.readFileSync(path, {encoding: "utf8"}).replace(/\r?\n|\r/g, "");
}
const token = readFileString("token.txt");
const channel = process.argv[2];
if(!channel) {
console.error("Usage: node index.js <channel id>");
process.exit(1);
}
const headers = {authorization: token};
async function request(before) {
const options = {
method: "GET",
headers: headers
};
const request = await fetch(
`https://discord.com/api/channels/${channel}/attachments`,
options
);
return await request.json();
}
let result;
async function go() {
let page = await request();
result = page;
while(page.length >= 100) {
page = await request(page[page.length - 1].id);
result = result.concat(page);
}
console.log(`Fetched ${result.length} images`);
fs.writeFileSync("links.json", JSON.stringify(result, null, 2));
}
go();
Output: Console
Fetched undefined images
Output: links.json
{
"message": "404: Not Found",
"code": 0
}
Any help of how I would get all image links in the links.json file would be appreciated
It seems at looking at the Docs It does not allow you to make a GET request for message attachments.

Categories