Merge branch 'dev'
commit
684f4f0fe9
|
@ -46,6 +46,7 @@
|
||||||
"nodemailer": "^6.4.8",
|
"nodemailer": "^6.4.8",
|
||||||
"pluris": "^0.2.5",
|
"pluris": "^0.2.5",
|
||||||
"signale": "^1.4.0",
|
"signale": "^1.4.0",
|
||||||
|
"stripe": "^8.120.0",
|
||||||
"uuid": "^8.0.0",
|
"uuid": "^8.0.0",
|
||||||
"yaml": "^1.9.2"
|
"yaml": "^1.9.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Server, ServerManagement } from '../../class';
|
import { Server, ServerManagement } from '../../class';
|
||||||
|
|
||||||
export default (management: ServerManagement) => {
|
export default (management: ServerManagement) => {
|
||||||
const server = new Server(management, 3890, `${__dirname}/routes`);
|
const server = new Server(management, 3890, `${__dirname}/routes`, false);
|
||||||
return server;
|
return server;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { TextChannel } from 'eris';
|
import axios from 'axios';
|
||||||
/* eslint-disable no-shadow */
|
import bodyParser from 'body-parser';
|
||||||
import jwt from 'jsonwebtoken';
|
import { Route, Server, LocalStorage } from '../../../class';
|
||||||
import { RichEmbed, Route, Server, LocalStorage } from '../../../class';
|
|
||||||
// import acknowledgements from '../../../configs/acknowledgements.json';
|
// import acknowledgements from '../../../configs/acknowledgements.json';
|
||||||
|
|
||||||
export default class Internal extends Route {
|
export default class Internal extends Route {
|
||||||
|
@ -79,5 +78,42 @@ export default class Internal extends Route {
|
||||||
return this.handleError(err, res);
|
return this.handleError(err, res);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.router.post('/sub', bodyParser.raw({ type: 'application/json' }), async (req, res) => {
|
||||||
|
try {
|
||||||
|
const event = this.server.client.stripe.webhooks.constructEvent(req.body, req.headers['stripe-signature'], this.server.client.config.stripeSubSigningSecret);
|
||||||
|
const data = <any> event.data.object;
|
||||||
|
|
||||||
|
switch (event.type) {
|
||||||
|
default:
|
||||||
|
return res.sendStatus(400);
|
||||||
|
case 'customer.subscription.created':
|
||||||
|
if (data.items.data[0].price.product === 'prod_Hi4EYmf2am5VZt') {
|
||||||
|
const customer = await this.server.client.db.Customer.findOne({ cusID: data.customer }).lean().exec();
|
||||||
|
if (!customer) return res.sendStatus(404);
|
||||||
|
await axios({
|
||||||
|
method: 'get',
|
||||||
|
url: `https://api.libraryofcode.org/wh/t2?userID=${customer.userID}&auth=${this.server.client.config.internalKey}`,
|
||||||
|
});
|
||||||
|
res.sendStatus(200);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'customer.subscription.deleted':
|
||||||
|
if (data.items.data[0].price.product === 'prod_Hi4EYmf2am5VZt') {
|
||||||
|
const customer = await this.server.client.db.Customer.findOne({ cusID: data.customer }).lean().exec();
|
||||||
|
if (!customer) return res.sendStatus(404);
|
||||||
|
await axios({
|
||||||
|
method: 'get',
|
||||||
|
url: `https://api.libraryofcode.org/wh/t2-rm?userID=${customer.userID}&auth=${this.server.client.config.internalKey}`,
|
||||||
|
});
|
||||||
|
return res.sendStatus(200);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (err) {
|
||||||
|
return this.handleError(err, res);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,43 @@ export default class Root extends Route {
|
||||||
public bind() {
|
public bind() {
|
||||||
this.router.get('/', (_req, res) => res.redirect('https://www.libraryofcode.org/'));
|
this.router.get('/', (_req, res) => res.redirect('https://www.libraryofcode.org/'));
|
||||||
|
|
||||||
|
this.router.get('/dash', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const lookup = await this.server.client.db.CustomerPortal.findOne({ key: req.query.q?.toString() });
|
||||||
|
if (!lookup) return res.status(404).json({ code: this.constants.codes.NOT_FOUND, message: this.constants.messages.NOT_FOUND });
|
||||||
|
if (new Date(lookup.expiresOn) < new Date()) return res.status(401).json({ code: this.constants.codes.UNAUTHORIZED, message: this.constants.messages.UNAUTHORIZED });
|
||||||
|
|
||||||
|
const customer = await this.server.client.db.Customer.findOne({ userID: lookup.userID });
|
||||||
|
if (!customer) {
|
||||||
|
const newCus = await this.server.client.stripe.customers.create({
|
||||||
|
email: lookup.emailAddress,
|
||||||
|
metadata: {
|
||||||
|
userID: lookup.userID,
|
||||||
|
username: lookup.username,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await (new this.server.client.db.Customer({
|
||||||
|
cusID: newCus.id,
|
||||||
|
userID: lookup.userID,
|
||||||
|
})).save();
|
||||||
|
const billingURL = await this.server.client.stripe.billingPortal.sessions.create({
|
||||||
|
customer: newCus.id,
|
||||||
|
return_url: 'https://www.libraryofcode.org',
|
||||||
|
});
|
||||||
|
res.redirect(302, billingURL.url);
|
||||||
|
return await lookup.updateOne({ $set: { used: true } });
|
||||||
|
}
|
||||||
|
const billingURL = await this.server.client.stripe.billingPortal.sessions.create({
|
||||||
|
customer: customer.cusID,
|
||||||
|
return_url: 'https://www.libraryofcode.org',
|
||||||
|
});
|
||||||
|
res.redirect(302, billingURL.url);
|
||||||
|
return await lookup.updateOne({ $set: { used: true } });
|
||||||
|
} catch (err) {
|
||||||
|
return this.handleError(err, res);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.router.get('/:key', async (req, res) => {
|
this.router.get('/:key', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const link: RedirectRaw = await this.server.client.db.Redirect.findOne({ key: req.params.key }).lean().exec();
|
const link: RedirectRaw = await this.server.client.db.Redirect.findOne({ key: req.params.key }).lean().exec();
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
import Stripe from 'stripe';
|
||||||
import eris from 'eris';
|
import eris from 'eris';
|
||||||
import pluris from 'pluris';
|
import pluris from 'pluris';
|
||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import { Collection, Command, LocalStorage, Queue, Util, ServerManagement, Event } from '.';
|
import { Collection, Command, LocalStorage, Queue, Util, ServerManagement, Event } from '.';
|
||||||
import { File, FileInterface, Member, MemberInterface, Merchant, MerchantInterface, Moderation, ModerationInterface, NNTrainingData, NNTrainingDataInterface, Note, NoteInterface, PagerNumber, PagerNumberInterface, Rank, RankInterface, Redirect, RedirectInterface, Score, ScoreInterface, ScoreHistorical, ScoreHistoricalInterface, Staff, StaffInterface, Stat, StatInterface } from '../models';
|
import { Customer, CustomerInterface, CustomerPortal, CustomerPortalInterface, File, FileInterface, Member, MemberInterface, Merchant, MerchantInterface, Moderation, ModerationInterface, NNTrainingData, NNTrainingDataInterface, Note, NoteInterface, PagerNumber, PagerNumberInterface, Rank, RankInterface, Redirect, RedirectInterface, Score, ScoreInterface, ScoreHistorical, ScoreHistoricalInterface, Staff, StaffInterface, Stat, StatInterface } from '../models';
|
||||||
import { Config } from '../../types'; // eslint-disable-line
|
import { Config } from '../../types'; // eslint-disable-line
|
||||||
|
|
||||||
pluris(eris);
|
pluris(eris);
|
||||||
|
@ -23,7 +24,9 @@ export default class Client extends eris.Client {
|
||||||
|
|
||||||
public queue: Queue;
|
public queue: Queue;
|
||||||
|
|
||||||
public db: { File: mongoose.Model<FileInterface>, Member: mongoose.Model<MemberInterface>, Merchant: mongoose.Model<MerchantInterface>, Moderation: mongoose.Model<ModerationInterface>, NNTrainingData: mongoose.Model<NNTrainingDataInterface>, Note: mongoose.Model<NoteInterface>, PagerNumber: mongoose.Model<PagerNumberInterface>, Rank: mongoose.Model<RankInterface>, Redirect: mongoose.Model<RedirectInterface>, Score: mongoose.Model<ScoreInterface>, ScoreHistorical: mongoose.Model<ScoreHistoricalInterface>, Staff: mongoose.Model<StaffInterface>, Stat: mongoose.Model<StatInterface>, local: { muted: LocalStorage } };
|
public stripe: Stripe;
|
||||||
|
|
||||||
|
public db: { Customer: mongoose.Model<CustomerInterface>, CustomerPortal: mongoose.Model<CustomerPortalInterface>, File: mongoose.Model<FileInterface>, Member: mongoose.Model<MemberInterface>, Merchant: mongoose.Model<MerchantInterface>, Moderation: mongoose.Model<ModerationInterface>, NNTrainingData: mongoose.Model<NNTrainingDataInterface>, Note: mongoose.Model<NoteInterface>, PagerNumber: mongoose.Model<PagerNumberInterface>, Rank: mongoose.Model<RankInterface>, Redirect: mongoose.Model<RedirectInterface>, Score: mongoose.Model<ScoreInterface>, ScoreHistorical: mongoose.Model<ScoreHistoricalInterface>, Staff: mongoose.Model<StaffInterface>, Stat: mongoose.Model<StatInterface>, local: { muted: LocalStorage } };
|
||||||
|
|
||||||
constructor(token: string, options?: eris.ClientOptions) {
|
constructor(token: string, options?: eris.ClientOptions) {
|
||||||
super(token, options);
|
super(token, options);
|
||||||
|
@ -31,7 +34,7 @@ export default class Client extends eris.Client {
|
||||||
this.events = new Collection<Event>();
|
this.events = new Collection<Event>();
|
||||||
this.intervals = new Collection<NodeJS.Timeout>();
|
this.intervals = new Collection<NodeJS.Timeout>();
|
||||||
this.queue = new Queue(this);
|
this.queue = new Queue(this);
|
||||||
this.db = { File, Member, Merchant, Moderation, NNTrainingData, Note, PagerNumber, Rank, Redirect, Score, ScoreHistorical, Staff, Stat, local: { muted: new LocalStorage('muted') } };
|
this.db = { Customer, CustomerPortal, File, Member, Merchant, Moderation, NNTrainingData, Note, PagerNumber, Rank, Redirect, Score, ScoreHistorical, Staff, Stat, local: { muted: new LocalStorage('muted') } };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -60,6 +63,7 @@ export default class Client extends eris.Client {
|
||||||
public loadPlugins() {
|
public loadPlugins() {
|
||||||
this.util = new Util(this);
|
this.util = new Util(this);
|
||||||
this.serverManagement = new ServerManagement(this);
|
this.serverManagement = new ServerManagement(this);
|
||||||
|
this.stripe = new Stripe(this.config.stripeKey, { apiVersion: null, typescript: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
public async loadIntervals() {
|
public async loadIntervals() {
|
||||||
|
|
|
@ -15,13 +15,17 @@ export default class Server {
|
||||||
|
|
||||||
private root: string;
|
private root: string;
|
||||||
|
|
||||||
constructor(parent: ServerManagement, port: number, routeRoot: string) {
|
protected parse: boolean;
|
||||||
|
|
||||||
|
constructor(parent: ServerManagement, port: number, routeRoot: string, parse = true) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.app = express();
|
this.app = express();
|
||||||
this.routes = new Collection<Route>();
|
this.routes = new Collection<Route>();
|
||||||
this.port = port;
|
this.port = port;
|
||||||
this.root = routeRoot;
|
this.root = routeRoot;
|
||||||
|
|
||||||
|
this.parse = parse;
|
||||||
|
|
||||||
this.init();
|
this.init();
|
||||||
this.loadRoutes();
|
this.loadRoutes();
|
||||||
}
|
}
|
||||||
|
@ -50,7 +54,9 @@ export default class Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
this.app.use(bodyParser.json());
|
if (this.parse) {
|
||||||
|
this.app.use(bodyParser.json());
|
||||||
|
}
|
||||||
this.app.set('trust proxy', 'loopback');
|
this.app.set('trust proxy', 'loopback');
|
||||||
this.app.use(helmet({
|
this.app.use(helmet({
|
||||||
hsts: false,
|
hsts: false,
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
import axios from 'axios';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { Message } from 'eris';
|
||||||
|
import { randomBytes } from 'crypto';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
import { Client, Command } from '../class';
|
||||||
|
import Billing_T2 from './billing_t2';
|
||||||
|
|
||||||
|
export default class Billing extends Command {
|
||||||
|
constructor(client: Client) {
|
||||||
|
super(client);
|
||||||
|
this.name = 'billing';
|
||||||
|
this.description = 'Pulls up your Billing Portal. You must have a CS Account to continue.';
|
||||||
|
this.usage = `${this.client.config.prefix}billing`;
|
||||||
|
this.subcmds = [Billing_T2];
|
||||||
|
this.permissions = 0;
|
||||||
|
this.guildOnly = false;
|
||||||
|
this.enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run(message: Message) {
|
||||||
|
try {
|
||||||
|
const response = <{
|
||||||
|
found: boolean,
|
||||||
|
emailAddress?: string,
|
||||||
|
tier?: number,
|
||||||
|
supportKey?: string,
|
||||||
|
}> (await axios.get(`https://api.cloud.libraryofcode.org/wh/info?id=${message.author.id}&authorization=${this.client.config.internalKey}`)).data;
|
||||||
|
if (!response.found) return this.error(message.channel, 'CS Account not found.');
|
||||||
|
|
||||||
|
const portalKey = randomBytes(50).toString('hex');
|
||||||
|
const uid = uuid();
|
||||||
|
const redirect = new this.client.db.Redirect({
|
||||||
|
key: uid,
|
||||||
|
to: `https://loc.sh/dash?q=${portalKey}`,
|
||||||
|
});
|
||||||
|
const portal = new this.client.db.CustomerPortal({
|
||||||
|
key: portalKey,
|
||||||
|
username: message.author.username,
|
||||||
|
userID: message.author.id,
|
||||||
|
emailAddress: response.emailAddress,
|
||||||
|
expiresOn: moment().add(5, 'minutes').toDate(),
|
||||||
|
used: false,
|
||||||
|
});
|
||||||
|
await portal.save();
|
||||||
|
await redirect.save();
|
||||||
|
|
||||||
|
const chan = await this.client.getDMChannel(message.author.id);
|
||||||
|
await chan.createMessage(`__***Billing Account Portal***__\nClick here: https://loc.sh/${uid}\n\nYou will be redirected to your billing portal, please note the link expires after 5 minutes.`);
|
||||||
|
return await this.success(message.channel, 'Your Billing Portal information has been DMed to you.');
|
||||||
|
} catch (err) {
|
||||||
|
return this.client.util.handleError(err, message, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
import axios from 'axios';
|
||||||
|
import { Message } from 'eris';
|
||||||
|
import { Client, Command } from '../class';
|
||||||
|
|
||||||
|
export default class Billing_T2 extends Command {
|
||||||
|
constructor(client: Client) {
|
||||||
|
super(client);
|
||||||
|
this.name = 't2';
|
||||||
|
this.description = 'Subscription to CS Tier 2.';
|
||||||
|
this.usage = `${this.client.config.prefix}billing t2`;
|
||||||
|
this.permissions = 0;
|
||||||
|
this.guildOnly = false;
|
||||||
|
this.enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run(message: Message) {
|
||||||
|
try {
|
||||||
|
const response = <{
|
||||||
|
found: boolean,
|
||||||
|
emailAddress?: string,
|
||||||
|
tier?: number,
|
||||||
|
supportKey?: string,
|
||||||
|
}> (await axios.get(`https://api.cloud.libraryofcode.org/wh/info?id=${message.author.id}&authorization=${this.client.config.internalKey}`)).data;
|
||||||
|
if (!response.found) return this.error(message.channel, 'CS Account not found.');
|
||||||
|
|
||||||
|
const customer = await this.client.db.Customer.findOne({ userID: message.author.id });
|
||||||
|
if (!customer) return this.error(message.channel, `You do not have a Customer Account. Please run \`${this.client.config.prefix}billing\`, once you visit the Billing Portal via the URL given to you, please try again.`);
|
||||||
|
|
||||||
|
const subscription = await this.client.stripe.subscriptions.create({
|
||||||
|
customer: customer.cusID,
|
||||||
|
payment_behavior: 'allow_incomplete',
|
||||||
|
items: [{ price: 'price_1H8e6ODatwI1hQ4WFVvX6Nda' }],
|
||||||
|
days_until_due: 1,
|
||||||
|
collection_method: 'send_invoice',
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.client.stripe.invoices.finalizeInvoice(subscription.latest_invoice.toString());
|
||||||
|
const invoice = await this.client.stripe.invoices.retrieve(subscription.latest_invoice.toString());
|
||||||
|
|
||||||
|
const chan = await this.client.getDMChannel(message.author.id);
|
||||||
|
await chan.createMessage(`__**Invoice for New Subscription**__\n${invoice.hosted_invoice_url}\n\n*Please click on the link above to pay for your subscription.*`);
|
||||||
|
return this.success(message.channel, 'Transaction processed.');
|
||||||
|
} catch (err) {
|
||||||
|
return this.client.util.handleError(err, message, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ export { default as addrank } from './addrank';
|
||||||
export { default as addredirect } from './addredirect';
|
export { default as addredirect } from './addredirect';
|
||||||
export { default as apply } from './apply';
|
export { default as apply } from './apply';
|
||||||
export { default as ban } from './ban';
|
export { default as ban } from './ban';
|
||||||
|
export { default as billing } from './billing';
|
||||||
export { default as delitem } from './delitem';
|
export { default as delitem } from './delitem';
|
||||||
export { default as delmerchant } from './delmerchant';
|
export { default as delmerchant } from './delmerchant';
|
||||||
export { default as delnote } from './delnote';
|
export { default as delnote } from './delnote';
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { Document, Schema, model } from 'mongoose';
|
||||||
|
|
||||||
|
export interface CustomerInterface extends Document {
|
||||||
|
cusID: string,
|
||||||
|
userID: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
const Customer: Schema = new Schema({
|
||||||
|
cusID: String,
|
||||||
|
userID: String,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default model<CustomerInterface>('Customer', Customer);
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { Document, Schema, model } from 'mongoose';
|
||||||
|
|
||||||
|
export interface CustomerPortalInterface extends Document {
|
||||||
|
key: string,
|
||||||
|
username: string,
|
||||||
|
userID: string,
|
||||||
|
emailAddress: string,
|
||||||
|
expiresOn: Date,
|
||||||
|
used?: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
const CustomerPortal: Schema = new Schema({
|
||||||
|
key: String,
|
||||||
|
username: String,
|
||||||
|
userID: String,
|
||||||
|
emailAddress: String,
|
||||||
|
expiresOn: Date,
|
||||||
|
used: Boolean,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default model<CustomerPortalInterface>('CustomerPortal', CustomerPortal);
|
|
@ -1,3 +1,5 @@
|
||||||
|
export { default as Customer, CustomerInterface } from './Customer';
|
||||||
|
export { default as CustomerPortal, CustomerPortalInterface } from './CustomerPortal';
|
||||||
export { default as File, FileInterface } from './File';
|
export { default as File, FileInterface } from './File';
|
||||||
export { default as Member, MemberInterface } from './Member';
|
export { default as Member, MemberInterface } from './Member';
|
||||||
export { default as Merchant, MerchantInterface } from './Merchant';
|
export { default as Merchant, MerchantInterface } from './Merchant';
|
||||||
|
|
|
@ -7,4 +7,6 @@ export declare interface Config {
|
||||||
webhookID: string;
|
webhookID: string;
|
||||||
webhookToken: string;
|
webhookToken: string;
|
||||||
internalKey: string;
|
internalKey: string;
|
||||||
|
stripeKey: string;
|
||||||
|
stripeSubSigningSecret: string;
|
||||||
}
|
}
|
||||||
|
|
18
yarn.lock
18
yarn.lock
|
@ -155,6 +155,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.1.tgz#5d93e0a099cd0acd5ef3d5bde3c086e1f49ff68c"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.1.tgz#5d93e0a099cd0acd5ef3d5bde3c086e1f49ff68c"
|
||||||
integrity sha512-FAYBGwC+W6F9+huFIDtn43cpy7+SzG+atzRiTfdp3inUKL2hXnd4rG8hylJLIh4+hqrQy1P17kvJByE/z825hA==
|
integrity sha512-FAYBGwC+W6F9+huFIDtn43cpy7+SzG+atzRiTfdp3inUKL2hXnd4rG8hylJLIh4+hqrQy1P17kvJByE/z825hA==
|
||||||
|
|
||||||
|
"@types/node@>=8.1.0":
|
||||||
|
version "14.14.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.6.tgz#146d3da57b3c636cc0d1769396ce1cfa8991147f"
|
||||||
|
integrity sha512-6QlRuqsQ/Ox/aJEQWBEJG7A9+u7oSYl3mem/K8IzxXG/kAGbV1YPD9Bg9Zw3vyxC/YP+zONKwy8hGkSt1jxFMw==
|
||||||
|
|
||||||
"@types/nodemailer@^6.4.0":
|
"@types/nodemailer@^6.4.0":
|
||||||
version "6.4.0"
|
version "6.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.0.tgz#d8c039be3ed685c4719a026455555be82c124b74"
|
resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.0.tgz#d8c039be3ed685c4719a026455555be82c124b74"
|
||||||
|
@ -2637,6 +2642,11 @@ qs@6.7.0:
|
||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
||||||
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
|
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
|
||||||
|
|
||||||
|
qs@^6.6.0:
|
||||||
|
version "6.9.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687"
|
||||||
|
integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==
|
||||||
|
|
||||||
qs@~6.5.2:
|
qs@~6.5.2:
|
||||||
version "6.5.2"
|
version "6.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||||
|
@ -3183,6 +3193,14 @@ strip-json-comments@~2.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||||
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
|
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
|
||||||
|
|
||||||
|
stripe@^8.120.0:
|
||||||
|
version "8.120.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.120.0.tgz#234a2fd7352c529532d32e76c75094068690a373"
|
||||||
|
integrity sha512-OzqyUWwdYPla1Onjn94pdGwqpVsOAOlNwo75Yr3T3n1eN17CMUclAVU9hIAqBrDVeyHOyriYklLRhT3NGr7BNw==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" ">=8.1.0"
|
||||||
|
qs "^6.6.0"
|
||||||
|
|
||||||
supports-color@^5.3.0:
|
supports-color@^5.3.0:
|
||||||
version "5.5.0"
|
version "5.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||||
|
|
Loading…
Reference in New Issue