Skip to content
This repository was archived by the owner on Apr 8, 2024. It is now read-only.

Commit 79fdaf5

Browse files
authored
Merge pull request #5 from uniquelyparticular/feat/addstripe
Feature: Added Stripe Usage Tracking
2 parents e1de38b + 3e06192 commit 79fdaf5

File tree

5 files changed

+113
-48
lines changed

5 files changed

+113
-48
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![npm version](https://img.shields.io/npm/v/@particular./stripe-distinct-usage-tracking.svg)](https://www.npmjs.com/package/@particular./stripe-distinct-usage-tracking) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier) [![CircleCI](https://img.shields.io/circleci/project/github/uniquelyparticular/stripe-distinct-usage-tracking.svg?label=circleci)](https://circleci.com/gh/uniquelyparticular/stripe-distinct-usage-tracking) ![dependency status: david](https://img.shields.io/david/uniquelyparticular/stripe-distinct-usage-tracking.svg)
44

5-
> Stripe usage tracking implementation to increment usage record only for distinct new entities
5+
> Stripe Metered Pricing unique usage tracking implementation to increment Subscriptions Usage Record only for distinct new entities
66
77
Built with [Micro](https://github.com/zeit/micro)! 🤩
88

now.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"env": {
66
"NODE_ENV": "production",
77
"USAGETRACKING_ORIGIN_WHITELIST": "@demo-usage-tracking-origin-whitelist",
8+
"GATEWAY_SK": "@particular-gateway-sk",
89
"FIREBASE_PROJECT_ID": "@particular-firebase-project-id",
910
"FIREBASE_CLIENT_ID": "@particular-firebase-client-id",
1011
"FIREBASE_PRIVATE_KEY_ID": "@particular-firebase-pk-id",

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@
66
"description": "Please contact Particular. via info@uniquelyparticular.com for any questions",
77
"keywords": [
88
"stripe",
9+
"subscriptions",
10+
"usage",
11+
"usage records",
12+
"usage tracking",
13+
"metered",
14+
"metered pricing",
915
"google",
1016
"firebase",
1117
"firestore",
1218
"microservices",
13-
"integration",
1419
"particular",
1520
"particular."
1621
],

src/index.js

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const { newThisPeriod } = require('./utils')
1+
const { createUsageRecords, newThisPeriod } = require('./utils')
22
const { json, send } = require('micro')
33
const { URL } = require('whatwg-url')
44
const cors = require('micro-cors')()
@@ -25,7 +25,7 @@ process.on('unhandledRejection', (reason, p) => {
2525

2626
const notAuthorized = async (req, res) =>
2727
send(res, 401, {
28-
error: 'Referer or Destination not whitelisted or insecure'
28+
error: 'Referer or Origin not whitelisted'
2929
})
3030
const invalidSecret = async (req, res) =>
3131
send(res, 401, {
@@ -77,6 +77,8 @@ const getOrigin = (origin, referer) => {
7777
if (subOrigin) {
7878
origin = decodeURIComponent(subOrigin[1])
7979
}
80+
console.log('origin', origin)
81+
console.log('referer', referer)
8082
return origin || referer
8183
}
8284

@@ -104,12 +106,26 @@ module.exports = cors(async (req, res) => {
104106

105107
try {
106108
const body = await json(req)
107-
// console.log('body',body)
108-
return newThisPeriod(body)
109+
const { applicationId, collectionId, subscription, tracked } = body
110+
111+
return newThisPeriod(applicationId, collectionId, subscription, tracked)
109112
.then(isNewThisPeriod => {
110-
return send(res, 200, {
111-
newThisPeriod: isNewThisPeriod
112-
})
113+
if (isNewThisPeriod) {
114+
return createUsageRecords(subscription.items)
115+
.then(() => {
116+
return send(res, 200, {
117+
newThisPeriod: isNewThisPeriod
118+
})
119+
})
120+
.catch(error => {
121+
const { raw, headers, ...jsonError } = _toJSON(error)
122+
return send(res, 500, jsonError)
123+
})
124+
} else {
125+
return send(res, 200, {
126+
newThisPeriod: isNewThisPeriod
127+
})
128+
}
113129
})
114130
.catch(error => {
115131
const jsonError = _toJSON(error)

src/utils.js

Lines changed: 82 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const moment = require('moment-timezone')
22
const admin = require('firebase-admin')
3+
const stripe = require('stripe')(process.env.GATEWAY_SK)
34

45
const _firebaseConfig = {
56
type: 'service_account',
@@ -30,46 +31,88 @@ if (!admin.apps.length) {
3031
}
3132
const firestore = admin.firestore()
3233

33-
exports.newThisPeriod = data => {
34-
return new Promise((resolve, reject) => {
35-
const trackedRef = firestore
36-
.collection('applications')
37-
.doc(`${data.applicationId}`)
38-
.collection(`${data.collectionId}`)
39-
.doc(`${data.subscription.id}`)
40-
.collection(
41-
`${moment
42-
.unix(data.subscription.current_period_start)
43-
.format('MMDDYYYY')}_${moment
44-
.unix(data.subscription.current_period_end)
45-
.format('MMDDYYYY')}`
46-
)
47-
.doc(`${data.tracked.id}`)
34+
module.exports = {
35+
newThisPeriod(applicationId, collectionId, subscription, tracked) {
36+
return new Promise((resolve, reject) => {
37+
const metadata = tracked.metadata
38+
const trackedRef = firestore
39+
.collection('applications')
40+
.doc(`${applicationId}`)
41+
.collection(`${collectionId}`)
42+
.doc(`${subscription.id}`)
43+
.collection(
44+
`${moment
45+
.unix(subscription.current_period_start)
46+
.format('MMDDYYYY')}_${moment
47+
.unix(subscription.current_period_end)
48+
.format('MMDDYYYY')}`
49+
)
50+
.doc(`${tracked.id}`)
4851

49-
return trackedRef
50-
.get()
51-
.then(billingPeriodTrackedRecord => {
52-
// console.log('billingPeriodTrackedRecord',billingPeriodTrackedRecord)
53-
return trackedRef
54-
.set(
55-
{
56-
value: data.tracked.value,
57-
usage: admin.firestore.FieldValue.arrayUnion(
58-
moment()
59-
.utc()
60-
.format()
61-
)
62-
},
63-
{ merge: true }
64-
)
65-
.then(() => {
66-
// console.log(
67-
// `trackedRef, newThisPeriod: ${!billingPeriodTrackedRecord.exists}`
68-
// )
69-
resolve(!billingPeriodTrackedRecord.exists)
70-
})
71-
.catch(error => reject(error))
52+
return trackedRef
53+
.get()
54+
.then(billingPeriodTrackedRecord => {
55+
// console.log('billingPeriodTrackedRecord',billingPeriodTrackedRecord)
56+
return trackedRef
57+
.set(
58+
{
59+
metadata,
60+
usage: admin.firestore.FieldValue.arrayUnion(
61+
moment()
62+
.utc()
63+
.format()
64+
)
65+
},
66+
{ merge: true }
67+
)
68+
.then(() => {
69+
// console.log(
70+
// `trackedRef, newThisPeriod: ${!billingPeriodTrackedRecord.exists}`
71+
// )
72+
resolve(!billingPeriodTrackedRecord.exists)
73+
})
74+
.catch(error => reject(error))
75+
})
76+
.catch(error => reject(error))
77+
})
78+
},
79+
80+
createUsageRecords(
81+
subscriptionItems,
82+
quantity = 1,
83+
timestamp = moment().unix()
84+
) {
85+
return new Promise((resolve, reject) => {
86+
const usageRecords = []
87+
subscriptionItems.map(subscriptionItem => {
88+
usageRecords.push(
89+
stripe.usageRecords
90+
.create(subscriptionItem.id, {
91+
quantity,
92+
timestamp
93+
})
94+
.then(handleError)
95+
)
7296
})
73-
.catch(error => reject(error))
97+
98+
return Promise.all(usageRecords)
99+
.then(responses => {
100+
resolve(responses)
101+
})
102+
.catch(error => reject(error))
103+
})
104+
}
105+
}
106+
107+
const handleError = response => {
108+
return new Promise((resolve, reject) => {
109+
if (response.error) {
110+
console.error(
111+
`handleError, response.error: ${JSON.stringify(response.error)}`
112+
)
113+
reject(response.error)
114+
} else {
115+
resolve(response)
116+
}
74117
})
75118
}

0 commit comments

Comments
 (0)