Skip to content

Instantly share code, notes, and snippets.

@michabbb
Last active January 27, 2023 22:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save michabbb/ae7c6446e35f577ef86643e125ef6a8f to your computer and use it in GitHub Desktop.
Save michabbb/ae7c6446e35f577ef86643e125ef6a8f to your computer and use it in GitHub Desktop.
lambda function to handle eBay Marketplace Account Deletion Notifications
const EventNotificationSDK = require('event-notification-nodejs-sdk');
const https = require('https');
// https://github.com/eBay/event-notification-nodejs-sdk/blob/main/lib/constants.js
const constants = {
ALGORITHM: 'ssl3-sha1',
AUTHORIZATION: 'Authorization',
BASE64: 'base64',
BEARER: 'bearer ',
ENVIRONMENT: {
SANDBOX: 'SANDBOX',
PRODUCTION: 'PRODUCTION'
},
HEADERS: {
APPLICATION_JSON: 'application/json'
},
HEX: 'hex',
HTTP_STATUS_CODE: {
NO_CONTENT: 204,
OK: 200,
PRECONDITION_FAILED: 412,
INTERNAL_SERVER_ERROR: 500
},
KEY_END: '-----END PUBLIC KEY-----',
KEY_PATTERN_END: /-----END PUBLIC KEY-----/,
KEY_PATTERN_START: /-----BEGIN PUBLIC KEY-----/,
KEY_START: '-----BEGIN PUBLIC KEY-----',
NOTIFICATION_API_ENDPOINT_PRODUCTION: 'https://api.ebay.com/commerce/notification/v1/public_key/',
NOTIFICATION_API_ENDPOINT_SANDBOX: 'https://api.sandbox.ebay.com/commerce/notification/v1/public_key/',
SHA256: 'sha256',
TOPICS: {
MARKETPLACE_ACCOUNT_DELETION: 'MARKETPLACE_ACCOUNT_DELETION'
},
X_EBAY_SIGNATURE: 'x-ebay-signature'
};
// https://github.com/eBay/event-notification-nodejs-sdk/blob/main/examples/config.json
const config = {
PRODUCTION: {
clientId: 'xxxxxxxxxxxxxxxxxxx',
clientSecret: 'xxxxxxxxxxxxxxxxxxx',
devid: 'xxxxxxxxxxxxxxxxxxx',
environment: 'PRODUCTION',
baseUrl: "api.ebay.com",
redirectUri: "xxxxxxxxxxxxxxxxxxx",
},
verificationToken: 'xxxxxxxxxxxxxxxxxxx',
endpoint: 'xxxxxxxxxxxxxxxxxxx',
};
const environment = 'PRODUCTION';
exports.handler = async function (event, context, done) {
let response;
let slack_status;
let slack_processResult;
if (event && event.requestContext.http.method === 'GET' && event.queryStringParameters && event.queryStringParameters.challenge_code) {
try {
const challengeResponse = EventNotificationSDK.validateEndpoint(
event.queryStringParameters.challenge_code,
config);
response = {
statusCode: constants.HTTP_STATUS_CODE.OK,
headers: {
"content-type": "application/json",
},
body: JSON.stringify({challengeResponse: challengeResponse})
};
slack_status = 'ok';
slack_processResult = 'Endpoint validation ok, challengeResponse: '+challengeResponse;
console.log("response: " + JSON.stringify(response));
} catch (e) {
// eslint-disable-next-line no-console
console.error(`Endpoint validation failure: ${e}`);
slack_status='failed';
slack_processResult = `Endpoint validation failure: ${e}`;
response = {
statusCode: constants.HTTP_STATUS_CODE.INTERNAL_SERVER_ERROR,
headers: {
"content-type": "application/json",
},
body: JSON.stringify(`Endpoint validation failure: ${e}`)
};
}
} else if (event && event.requestContext.http.method === 'POST') {
let bodyParsed = JSON.parse(event.body);
let responseCode = 0;
try {
responseCode = await EventNotificationSDK.process(
bodyParsed,
event.headers[constants.X_EBAY_SIGNATURE],
config,
environment
);
} catch(ex) {
console.error(`Signature validation processing failure: ${ex}\n`);
slack_status='failed';
slack_processResult = `Signature validation processing failure: ${ex}`;
response = {
headers: {
"content-type": "application/json",
},
statusCode: constants.HTTP_STATUS_CODE.INTERNAL_SERVER_ERROR,
body: JSON.stringify(`Signature validation processing failure: ${ex}`)
};
}
if (responseCode === constants.HTTP_STATUS_CODE.NO_CONTENT) {
console.log(`Message processed successfully for: \n- Topic: ${bodyParsed.metadata.topic} \n- NotificationId: ${bodyParsed.notification.notificationId}\n`);
slack_status='ok';
slack_processResult = `Message processed successfully`;
} else if (responseCode === constants.HTTP_STATUS_CODE.PRECONDITION_FAILED) {
console.error(`Signature mismatch for: \n- Payload: ${event.body} \n- Signature: ${event.headers[constants.X_EBAY_SIGNATURE]}\n`);
slack_status='failed';
slack_processResult = `Signature mismatch`;
}
response = {
statusCode: responseCode
};
} else {
response = {
statusCode: constants.HTTP_STATUS_CODE.INTERNAL_SERVER_ERROR,
headers: {
"content-type": "application/json",
},
body: JSON.stringify('missing input data')
};
slack_status='failed';
slack_processResult = `missing input data`;
}
console.log(response);
return response;
/*
return await notifySlack(event,slack_status,slack_processResult).then(res => {
console.log(response);
return response;
}).catch(err => {
console.log(err);
console.log(response);
return response;
});
*/
};
async function notifySlack(event, status, processResultText) {
console.log('send info to slack');
let color = 'yellow';
if (status==="ok") {
color = '#138f17';
} else {
color = '#f54248';
}
const options = {
hostname: "hooks.slack.com",
method: "POST",
path: "/services/xxxxxxxxxxxxxxxxxxxxxxxx",
};
const payload = JSON.stringify({
channel: '@username or your #slack-channel',
attachments: [{
title: `${processResultText}`,
author_name: 'ebay-account-deletion-notify-handler',
text: 'Account delete Request by eBay ```'+JSON.stringify(event)+'```',
color: `${color}`
}]
});
console.log(payload);
return new Promise((resolve,reject) => {
const r = https.request(options, function (res) {
res.setEncoding('utf8');
res.on('data', function (data) {
console.log('SLACK SEND OK');
resolve('SLACK SEND OK');
});
}).on("error", function (e) {
console.log('SLACK SEND FAILED ' + e);
reject('SLACK SEND FAILED');
});
r.write(payload);
r.end();
});
}
@jarviss12
Copy link

Mate it seems It doesn't work now. Please help. I got the following error:

Test Event Name
AccountDelete

Response
{
  "errorType": "TypeError",
  "errorMessage": "Cannot read properties of undefined (reading 'http')",
  "trace": [
    "TypeError: Cannot read properties of undefined (reading 'http')",
    "    at Runtime.exports.handler (/var/task/index.js:63:39)",
    "    at Runtime.handleOnce (file:///var/runtime/index.mjs:548:29)"
  ]
}

Function Logs
START RequestId: 6939f14d-0e5a-4289-ae55-375fb287690f Version: $LATEST
2022-05-30T04:32:33.682Z	6939f14d-0e5a-4289-ae55-375fb287690f	ERROR	Invoke Error 	{"errorType":"TypeError","errorMessage":"Cannot read properties of undefined (reading 'http')","stack":["TypeError: Cannot read properties of undefined (reading 'http')","    at Runtime.exports.handler (/var/task/index.js:63:39)","    at Runtime.handleOnce (file:///var/runtime/index.mjs:548:29)"]}
END RequestId: 6939f14d-0e5a-4289-ae55-375fb287690f
REPORT RequestId: 6939f14d-0e5a-4289-ae55-375fb287690f	Duration: 23.67 ms	Billed Duration: 24 ms	Memory Size: 128 MB	Max Memory Used: 65 MB	Init Duration: 183.21 ms

Request ID
6939f14d-0e5a-4289-ae55-375fb287690f

Also I can't enter the production's client id and secret because it is disabled like this:
https://imgur.com/a/HCkxs71

@michabbb
Copy link
Author

@jarviss12 i guess you should contact ebay developer support first, to activate your keyset.

@tuckerfischer
Copy link

Thank you so much for posting this! One call out is for the events I had to use ' event.httpMethod' instead of 'event.requestContext.http.method'

@shakirblouch
Copy link

Can I get this code with php?

@michabbb
Copy link
Author

@shakirblouch actually you don't really need to do all this over complicated endpoint validation stuff. Ebay sends a simple xml to your endpoint, if you just return a 200, eBay is happy. it's up to you to validate, if the request really is from eBay. as long nobody knows your endpoint, I don't see any sense in investing a minute for someone to fake such a request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment