@cocalc/server
Version:
CoCalc server functionality: functions used by either the hub and the next.js server
97 lines (95 loc) • 4.5 kB
JavaScript
;
/*
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
* License: AGPLv3 s.t. "Commons Clause" – see LICENSE.md for details
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/*
Handle purchasing a licenses by customers. This is the server side of
@cocalc/frontend/site-licenses/purchase/
What this does:
- stores the request object in a table in the database
- if the request is for a quote, sends an email
- if the request is to make a purchase, makes that purchase and creates the license
*/
const database_1 = require("@cocalc/database");
const pool_1 = __importDefault(require("@cocalc/database/pool"));
const sanity_checks_1 = require("@cocalc/util/licenses/purchase/sanity-checks");
const charge_1 = require("./charge");
const create_license_1 = __importDefault(require("./create-license"));
const client_1 = require("@cocalc/server/stripe/client");
const async_utils_1 = require("@cocalc/util/async-utils");
const awaiting_1 = require("awaiting");
const logger_1 = require("@cocalc/backend/logger");
const logger = (0, logger_1.getLogger)("purchase-license");
// Does what should be done, and returns the license_id of the license that was created
// and has user added to as a manager.
// We don't allow a user to attempt a purchase more than once every THROTTLE_S seconds.
// This is just standard good practice, and avoids "double clicks" and probably some
// sort of attacks...
const THROTTLE_S = 15;
const last_attempt = {};
async function purchaseLicense(account_id, info, noThrottle) {
logger.debug("purchase_license: info=", info, ", account_id=", account_id);
if (!noThrottle) {
const now = Date.now();
if (now - (last_attempt[account_id] ?? 0) <= THROTTLE_S * 1000) {
throw Error("You must wait at least " +
THROTTLE_S.toString() +
" seconds between license purchases.");
}
last_attempt[account_id] = now;
}
logger.debug("purchase_license: running sanity checks...");
await (0, sanity_checks_1.sanity_checks)((0, pool_1.default)(), info);
logger.debug("purchase_license: charging user for license...");
const stripe = new client_1.StripeClient({ account_id });
const purchase = await (0, charge_1.chargeUserForLicense)(stripe, info);
logger.debug("purchase_license: creating the license...");
const database = (0, database_1.db)();
const license_id = await (0, create_license_1.default)(database, account_id, info);
logger.debug("purchase_license: set metadata on purchase...");
await (0, charge_1.setPurchaseMetadata)(purchase, { license_id, account_id });
// We have to try a few times, since the metadata sometimes doesn't appear
// when querying stripe for the customer, even after it was written in the
// above line. Also, this gives the credit card a first chance to work.
// This is ONLY for subscriptions.
if (info.subscription != "no") {
let done = false;
let delay_s = 1;
for (let i = 0; i < 20; i++) {
const customer = await (0, async_utils_1.callback2)(database.stripe_update_customer, {
account_id,
});
const data = customer?.subscriptions?.data;
if (data != null) {
for (const sub of data) {
if (sub.metadata?.license_id == license_id &&
sub.status == "active") {
// metadata is set and status is active -- yes
done = true;
break;
}
}
}
if (done) {
logger.debug("purchase_license: successfully verified metadata properly set and sub is active...");
break;
}
else {
logger.debug("purchase_license: trying again to verify metadata properly set and sub is active...");
}
await (0, awaiting_1.delay)(delay_s * 1000);
delay_s *= 1.1;
}
// Sets the license expire date if the subscription is NOT
// active at this point (e.g., due to credit card failure).
await database.sync_site_license_subscriptions(account_id);
}
return license_id;
}
exports.default = purchaseLicense;
//# sourceMappingURL=index.js.map