Skip to content

Commit 5b56da9

Browse files
Merge pull request #15 from SystangoTechnologies/feat-billing-flow
feat: webhook integration for billing flow
2 parents 06f0fd4 + 41090d7 commit 5b56da9

File tree

7 files changed

+97
-46
lines changed

7 files changed

+97
-46
lines changed

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ Note : Take values of APP_ID, WEBHOOK_PROXY_URL, WEBHOOK_SECRET, PRIVATE_KEY fro
5050

5151
Step 4. Run `npm start` to start the application
5252

53+
## Resources
54+
[Purchase Flow](https://developer.github.com/marketplace/integrating-with-the-github-marketplace-api/handling-new-purchases-and-free-trials/)
55+
56+
[Identifying and authorizing users for GitHub Apps](https://developer.github.com/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps/)
57+
58+
[Integrating with the GitHub Marketplace API](https://developer.github.com/marketplace/integrating-with-the-github-marketplace-api/)
59+
60+
5361
## Contributors
5462
[Anshul Soni](https://www.linkedin.com/in/anshul-soni-3903a2101/)
5563

constants.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,6 @@ module.exports = {
4343
MARKETPLACE_PURCHASE: 'marketplace_purchase',
4444
CHECK_SUITE: 'check_suite'
4545
},
46-
OAUTH_ENDPOINT: 'https://github.com/login/oauth'
46+
OAUTH_ENDPOINT: 'https://github.com/login/oauth',
47+
INSTALLATION_PATH: 'https://github.com/settings/installations'
4748
};

controllers/marketplace.js

+35-6
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,33 @@ const rp = require('request-promise');
55
/**
66
* Constants
77
*/
8-
const { OAUTH_ENDPOINT } = require('../constants');
8+
const { OAUTH_ENDPOINT, INSTALLATION_PATH } = require('../constants');
99

1010
/**
1111
* Controllers
1212
*/
1313
module.exports.marketplaceEventHandlers = async (req, res) => {
1414
try {
1515
let body = req.body;
16+
console.log('marketplaceEventHandlers ==> ', body);
1617
if(body.action && eventHandlers[body.action]) {
1718
await eventHandlers[body.action](req, res, body);
1819
}
1920
} catch (error) {
20-
console.log(error);
21+
console.log('Error while marketplaceEventHandlers => ', error);
2122
}
2223
};
2324

2425
module.exports.getAccessToken = async (req, res) => {
2526
try {
2627
const query = req.query;
28+
console.log('AccessToken API call started');
2729
const getAccessToken = `${OAUTH_ENDPOINT}/access_token?client_id=${process.env.CLIENT_ID}&client_secret=${process.env.CLIENT_SECRET}&code=${query.code}&redirect_uri=${process.env.REDIRECT_URI}&state=${query.STATE}`;
2830
let accessToken = await rp(getAccessToken);
29-
res.json(accessToken);
31+
console.log('AccessToken API call completed');
32+
res.redirect(INSTALLATION_PATH);
3033
} catch (error) {
31-
console.log(error);
34+
console.log('Error while getAccessToken => ', error);
3235
}
3336
};
3437

@@ -39,7 +42,33 @@ module.exports.getAccessToken = async (req, res) => {
3942
let eventHandlers = {
4043
purchased: async (req, res, eventData) => {
4144
let purchaserData = eventData.marketplace_purchase;
42-
const oAuthUrl = `${OAUTH_ENDPOINT}/authorize?client_id=${process.env.CLIENT_ID}&redirect_uri=${process.env.REDIRECT_URI}&state=${process.env.STATE}&login=${purchaserData.account.login}`;
43-
res.redirect(oAuthUrl);
45+
console.log('Event purchased triggered', purchaserData);
46+
/**
47+
* TODO: Authenticate user with github in future if we maintian user information in our database and we need to call user specific detials from github.
48+
*/
49+
// const oAuthUrl = `${OAUTH_ENDPOINT}/authorize?client_id=${process.env.CLIENT_ID}&redirect_uri=${process.env.REDIRECT_URI}&state=${process.env.STATE}&login=${purchaserData.account.login}`;
50+
// console.log('oAuthUrl', oAuthUrl);
51+
// res.redirect(oAuthUrl);
52+
return res.json().status(200);
53+
},
54+
cancelled: async (req, res, eventData) => {
55+
let purchaserData = eventData.marketplace_purchase;
56+
console.log('Event cancelled triggered', purchaserData);
57+
return res.json().status(200);
58+
},
59+
pending_change: async (req, res, eventData) => {
60+
let purchaserData = eventData.marketplace_purchase;
61+
console.log('Event pending_change triggered', purchaserData);
62+
return res.json().status(200);
63+
},
64+
pending_change_cancelled: async (req, res, eventData) => {
65+
let purchaserData = eventData.marketplace_purchase;
66+
console.log('Event pending_change_cancelled triggered', purchaserData);
67+
return res.json().status(200);
68+
},
69+
changed: async (req, res, eventData) => {
70+
let purchaserData = eventData.marketplace_purchase;
71+
console.log('Event changed triggered', purchaserData);
72+
return res.json().status(200);
4473
}
4574
};

index.js

+20-7
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,31 @@ const express = require('express');
55
const expressApp = express();
66
const path = require('path');
77
const bodyParser = require('body-parser');
8-
8+
const {
9+
verifyWebhookData
10+
} = require('./middleware/verifyWebhooks');
911
/**
1012
* Controllers
1113
*/
12-
const { commitAndTitleValidator } = require('./controllers/pullRequest');
13-
const { marketplaceEventHandlers, getAccessToken } = require('./controllers/marketplace');
14-
const { listForSuite } = require('./controllers/checks');
14+
const {
15+
commitAndTitleValidator
16+
} = require('./controllers/pullRequest');
17+
const {
18+
marketplaceEventHandlers,
19+
getAccessToken
20+
} = require('./controllers/marketplace');
21+
const {
22+
listForSuite
23+
} = require('./controllers/checks');
1524

1625
/**
1726
* Constants
1827
*/
19-
const { configFileName, messages, events } = require('./constants.js');
28+
const {
29+
configFileName,
30+
messages,
31+
events
32+
} = require('./constants.js');
2033
const publicDirectory = path.join(`${__dirname}`, 'public');
2134

2235
/**
@@ -110,7 +123,7 @@ module.exports = async app => {
110123
* Marketplace API
111124
* This API gets hit by github on any marketplace event
112125
*/
113-
expressApp.post('/marketplace', marketplaceEventHandlers);
126+
expressApp.post('/marketplace', verifyWebhookData, marketplaceEventHandlers);
114127

115128
/**
116129
* This API gets hit when redirected by `POST: /marketplace` API
@@ -119,4 +132,4 @@ module.exports = async app => {
119132
expressApp.get('/auth', getAccessToken);
120133

121134
app.router.use('/', expressApp);
122-
};
135+
};

middleware/verifyWebhooks.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const crypto = require('crypto')
2+
const secret = process.env.WEBHOOK_SECRET;
3+
const sigHeaderName = 'x-hub-signature'
4+
5+
module.exports.verifyWebhookData = function(req, res, next) {
6+
const payload = JSON.stringify(req.body)
7+
if (!payload) {
8+
return next('Request body empty')
9+
}
10+
11+
const sig = req.get(sigHeaderName) || ''
12+
const hmac = crypto.createHmac('sha1', secret)
13+
const digest = Buffer.from('sha1=' + hmac.update(payload).digest('hex'), 'utf8')
14+
const checksum = Buffer.from(sig, 'utf8')
15+
if (checksum.length !== digest.length || !crypto.timingSafeEqual(digest, checksum)) {
16+
return next(`Request body digest (${digest}) did not match ${sigHeaderName} (${checksum})`)
17+
}
18+
return next()
19+
}

package-lock.json

+12-31
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "commit-message-lint-app",
3-
"version": "3.0.0",
3+
"version": "3.1.0",
44
"description": "An app for validating commit messages and pull requests title",
55
"author": "Anshul Soni <[email protected]>",
66
"license": "ISC",

0 commit comments

Comments
 (0)