Add ability to transfer organisation ownership
This commit is contained in:
parent
c9307f2b49
commit
050b03f9aa
|
@ -107,11 +107,11 @@ export class InviteView extends Routing(StateMixin(LitElement)) {
|
|||
this._resendButton.start();
|
||||
let org = this._org!;
|
||||
try {
|
||||
this._resendButton?.success();
|
||||
const newInvite = (await app.createInvites(org, [this._invite.email], this._invite.purpose))[0];
|
||||
this.go(`orgs/${this.orgId}/invites/${newInvite.id}`, undefined, true);
|
||||
this._resendButton.success();
|
||||
} catch (e) {
|
||||
this._resendButton.fail();
|
||||
this._resendButton?.fail();
|
||||
alert(e.message, { type: "warning" });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,8 +152,9 @@ export class MemberView extends Routing(StateMixin(LitElement)) {
|
|||
this.requestUpdate();
|
||||
} catch (e) {
|
||||
this._saveButton?.fail();
|
||||
alert(e.message || $l("Something went wrong. Please try again later!"), { type: "warning" });
|
||||
throw e;
|
||||
alert(e.message || $l("Something went wrong while processing your request. Please try again later!"), {
|
||||
type: "warning",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,7 +179,9 @@ export class MemberView extends Routing(StateMixin(LitElement)) {
|
|||
this._saveButton.success();
|
||||
} catch (e) {
|
||||
this._saveButton.fail();
|
||||
throw e;
|
||||
alert(e.message || $l("Something went wrong while processing your request. Please try again later!"), {
|
||||
type: "warning",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,7 +205,33 @@ export class MemberView extends Routing(StateMixin(LitElement)) {
|
|||
this.requestUpdate();
|
||||
} catch (e) {
|
||||
this._saveButton.fail();
|
||||
throw e;
|
||||
alert(e.message || $l("Something went wrong while processing your request. Please try again later!"), {
|
||||
type: "warning",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _makeOwner() {
|
||||
const member = this._member!;
|
||||
const confirmed = await confirm(
|
||||
$l("Are you sure you want to transfer this organizations ownership to {0}?", member.name || member.email),
|
||||
$l("Make Owner"),
|
||||
$l("Cancel")
|
||||
);
|
||||
if (confirmed) {
|
||||
this._saveButton.start();
|
||||
|
||||
try {
|
||||
await app.transferOwnership(this._org!, member);
|
||||
this._saveButton.success();
|
||||
this.requestUpdate();
|
||||
alert($l("The organization ownership was transferred successfully!"), { type: "success" });
|
||||
} catch (e) {
|
||||
this._saveButton.fail();
|
||||
alert(e.message || $l("Something went wrong while processing your request. Please try again later!"), {
|
||||
type: "warning",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -224,7 +253,9 @@ export class MemberView extends Routing(StateMixin(LitElement)) {
|
|||
this.requestUpdate();
|
||||
} catch (e) {
|
||||
this._saveButton.fail();
|
||||
throw e;
|
||||
alert(e.message || $l("Something went wrong while processing your request. Please try again later!"), {
|
||||
type: "warning",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -246,7 +277,9 @@ export class MemberView extends Routing(StateMixin(LitElement)) {
|
|||
this.requestUpdate();
|
||||
} catch (e) {
|
||||
this._saveButton.fail();
|
||||
throw e;
|
||||
alert(e.message || $l("Something went wrong while processing your request. Please try again later!"), {
|
||||
type: "warning",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -373,6 +406,17 @@ export class MemberView extends Routing(StateMixin(LitElement)) {
|
|||
<div class="ellipsis">${$l("Remove Admin")}</div>
|
||||
</div>
|
||||
`}
|
||||
${accountIsOwner && !isOwner
|
||||
? html`
|
||||
<div
|
||||
class="small double-padded list-item center-aligning spacing horizontal layout hover click"
|
||||
@click=${this._makeOwner}
|
||||
>
|
||||
<pl-icon icon="owner"></pl-icon>
|
||||
<div class="ellipsis">${$l("Make Owner")}</div>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</pl-list>
|
||||
</pl-popover>
|
||||
</header>
|
||||
|
|
|
@ -339,6 +339,7 @@ export class Menu extends Routing(StateMixin(LitElement)) {
|
|||
: ""}
|
||||
${app.orgs.map((org) => {
|
||||
const vaults = app.vaults.filter((v) => v.org && v.org.id === org.id);
|
||||
const isAdmin = org.isAdmin(app.account!);
|
||||
|
||||
return html`
|
||||
<div>
|
||||
|
@ -352,6 +353,7 @@ export class Menu extends Routing(StateMixin(LitElement)) {
|
|||
<pl-button
|
||||
class="small transparent round slim negatively-margined reveal-on-hover"
|
||||
@click=${(e: Event) => this._goTo(`orgs/${org.id}`, undefined, e)}
|
||||
?hidden=${!isAdmin}
|
||||
>
|
||||
<pl-icon icon="settings"></pl-icon>
|
||||
</pl-button>
|
||||
|
@ -388,6 +390,7 @@ export class Menu extends Routing(StateMixin(LitElement)) {
|
|||
<div
|
||||
class="menu-item subtle"
|
||||
@click=${() => this._goTo(`orgs/${org.id}/vaults/new`)}
|
||||
?hidden=${!isAdmin}
|
||||
>
|
||||
<pl-icon icon="add"></pl-icon>
|
||||
|
||||
|
|
|
@ -135,22 +135,33 @@ export class OrgSettingsView extends Routing(StateMixin(LitElement)) {
|
|||
<pl-scroller class="stretch">
|
||||
<div class="vertical center-aligning padded layout">
|
||||
<div class="vertical spacing layout fill-horizontally max-width-30em">
|
||||
<section class="padded vertical spacing layout">
|
||||
<h2 class="margined section-header">${$l("Security")}</h2>
|
||||
<section class="margined box">
|
||||
<h2 class="padded uppercase bg-dark border-bottom semibold">${$l("Security")}</h2>
|
||||
|
||||
<pl-button id="rotateKeysButton" @click=${this._rotateKeys}>
|
||||
${$l("Rotate Cryptographic Keys")}
|
||||
</pl-button>
|
||||
<div>
|
||||
<div class="half-padded list-item">
|
||||
<pl-button id="rotateKeysButton" @click=${this._rotateKeys}>
|
||||
${$l("Rotate Cryptographic Keys")}
|
||||
</pl-button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="padded vertical spacing layout">
|
||||
<h2 class="margined section-header">${$l("General")}</h2>
|
||||
<section class="margined box">
|
||||
<h2 class="padded uppercase bg-dark border-bottom semibold">${$l("More")}</h2>
|
||||
|
||||
<pl-button @click=${this._changeName}> ${$l("Change Organization Name")} </pl-button>
|
||||
|
||||
<pl-button class="negative" @click=${this._deleteOrg}>
|
||||
${$l("Delete Organization")}
|
||||
</pl-button>
|
||||
<div>
|
||||
<div class="half-padded list-item">
|
||||
<pl-button @click=${this._changeName}>
|
||||
${$l("Change Organization Name")}
|
||||
</pl-button>
|
||||
</div>
|
||||
<div class="half-padded list-item">
|
||||
<pl-button class="negative" @click=${this._deleteOrg}>
|
||||
${$l("Delete Organization")}
|
||||
</pl-button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -509,11 +509,6 @@ export class App {
|
|||
await this.syncVaults();
|
||||
await this.save();
|
||||
|
||||
// const autoCreateOrg = await this.authInfo?.provisioning.orgs.find((org) => org.autoCreate);
|
||||
// if (autoCreateOrg && !this.orgs.find((org) => org.id === autoCreateOrg.orgId)) {
|
||||
// await this.createOrg(autoCreateOrg?.orgName || $l("My Org"));
|
||||
// }
|
||||
|
||||
this.setStats({ lastSync: new Date() });
|
||||
this.publish();
|
||||
}
|
||||
|
@ -1140,7 +1135,7 @@ export class App {
|
|||
|
||||
// If the revision to be fetched matches the revision stored locally,
|
||||
// we don't need to fetch anything
|
||||
if (localVault && revision && localVault.revision === revision) {
|
||||
if (localVault && revision && localVault.revision === revision && !localVault.error) {
|
||||
return localVault;
|
||||
}
|
||||
|
||||
|
@ -1191,6 +1186,8 @@ export class App {
|
|||
|
||||
this._migrateFavorites(result);
|
||||
|
||||
result.error = undefined;
|
||||
|
||||
await this.saveVault(result);
|
||||
|
||||
return result;
|
||||
|
@ -1787,6 +1784,19 @@ export class App {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfers an organizations ownership to a different member
|
||||
*/
|
||||
async transferOwnership(org: Org, member: OrgMember) {
|
||||
if (!this.account || this.account.locked) {
|
||||
throw "App needs to be logged in and unlocked to transfer an organizations ownership!";
|
||||
}
|
||||
await this.updateOrg(org.id, async (org) => {
|
||||
await org.unlock(this.account as UnlockedAccount);
|
||||
await org.makeOwner(member);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* ===================
|
||||
* INVITE MANAGEMENT
|
||||
|
|
|
@ -435,7 +435,6 @@ export class Org extends SharedContainer implements Storable {
|
|||
await this.generateKeys();
|
||||
|
||||
// Rotate org encryption key
|
||||
delete this.encryptedData;
|
||||
await this.updateAccessors(this.members.filter((m) => m.role === OrgRole.Owner));
|
||||
|
||||
// Re-sign all members
|
||||
|
@ -568,8 +567,8 @@ export class Org extends SharedContainer implements Storable {
|
|||
/**
|
||||
* Removes a member from the organization
|
||||
*/
|
||||
async removeMember(member: { id: AccountID }) {
|
||||
if (!this.privateKey) {
|
||||
async removeMember(member: { id: AccountID }, reSignMembers = true) {
|
||||
if (reSignMembers && !this.privateKey) {
|
||||
throw "Organisation needs to be unlocked first.";
|
||||
}
|
||||
|
||||
|
@ -581,16 +580,46 @@ export class Org extends SharedContainer implements Storable {
|
|||
// Remove member
|
||||
this.members = this.members.filter((m) => m.id !== member.id);
|
||||
|
||||
// Verify remaining members (since we're going to re-sign them)
|
||||
if (reSignMembers) {
|
||||
// Verify remaining members (since we're going to re-sign them)
|
||||
await this.verifyAll();
|
||||
|
||||
// Bump minimum update date
|
||||
this.minMemberUpdated = new Date();
|
||||
|
||||
// Re-sign all members
|
||||
await Promise.all(
|
||||
this.members.filter((m) => m.role !== OrgRole.Suspended).map((m) => this.addOrUpdateMember(m))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfers organization ownership to a different member
|
||||
*/
|
||||
async makeOwner(member: { id: AccountID }) {
|
||||
if (!this.privateKey) {
|
||||
throw "Organisation needs to be unlocked first.";
|
||||
}
|
||||
|
||||
// Verify members and groups with current public key
|
||||
await this.verifyAll();
|
||||
|
||||
// Bump minimum update date
|
||||
this.minMemberUpdated = new Date();
|
||||
const newOwner = this.getMember(member);
|
||||
const existingOwner = this.getMember({ id: this.owner })!;
|
||||
|
||||
// Re-sign all members
|
||||
await Promise.all(
|
||||
this.members.filter((m) => m.role !== OrgRole.Suspended).map((m) => this.addOrUpdateMember(m))
|
||||
);
|
||||
if (!newOwner || !existingOwner) {
|
||||
throw "New and/or existing owner not found.";
|
||||
}
|
||||
|
||||
newOwner.role = OrgRole.Owner;
|
||||
existingOwner.role = OrgRole.Admin;
|
||||
this.owner = newOwner.id;
|
||||
|
||||
await this.addOrUpdateMember(newOwner);
|
||||
await this.addOrUpdateMember(existingOwner);
|
||||
|
||||
await this.updateAccessors(this.members.filter((m) => m.role === OrgRole.Owner));
|
||||
}
|
||||
|
||||
toString() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Account, AccountID } from "./account";
|
||||
import { AsSerializable, Serializable } from "./encoding";
|
||||
import { ErrorCode } from "./error";
|
||||
import { OrgID } from "./org";
|
||||
import { Err, ErrorCode } from "./error";
|
||||
import { OrgID, OrgInfo } from "./org";
|
||||
import { Storable, Storage } from "./storage";
|
||||
import { getIdFromEmail } from "./util";
|
||||
|
||||
|
@ -192,7 +192,12 @@ export class Provisioning extends Serializable {
|
|||
export interface Provisioner {
|
||||
getProvisioning(params: { email: string; accountId?: AccountID }): Promise<Provisioning>;
|
||||
accountDeleted(params: { email: string; accountId?: AccountID }): Promise<void>;
|
||||
orgDeleted(params: { id: OrgID }): Promise<void>;
|
||||
orgDeleted(params: OrgInfo): Promise<void>;
|
||||
orgOwnerChanged(
|
||||
org: OrgInfo,
|
||||
prevOwner: { email: string; id: AccountID },
|
||||
newOwner: { email: string; id: AccountID }
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
export class StubProvisioner implements Provisioner {
|
||||
|
@ -201,7 +206,12 @@ export class StubProvisioner implements Provisioner {
|
|||
}
|
||||
|
||||
async accountDeleted(_params: { email: string; accountId?: string }) {}
|
||||
async orgDeleted(_params: { id: OrgID }) {}
|
||||
async orgDeleted(_params: OrgInfo) {}
|
||||
async orgOwnerChanged(
|
||||
_org: { id: string },
|
||||
_prevOwner: { email: string; id: string },
|
||||
_newOwner: { email: string; id: string }
|
||||
): Promise<void> {}
|
||||
}
|
||||
|
||||
export class BasicProvisioner implements Provisioner {
|
||||
|
@ -227,8 +237,9 @@ export class BasicProvisioner implements Provisioner {
|
|||
}
|
||||
|
||||
const account =
|
||||
provisioning.account.accountId &&
|
||||
(await this.storage.get(Account, provisioning.account.accountId).catch(() => null));
|
||||
(provisioning.account.accountId &&
|
||||
(await this.storage.get(Account, provisioning.account.accountId).catch(() => null))) ||
|
||||
null;
|
||||
|
||||
const orgIds = account
|
||||
? [...new Set([...provisioning.account.orgs, ...account.orgs.map((org) => org.id)])]
|
||||
|
@ -263,18 +274,47 @@ export class BasicProvisioner implements Provisioner {
|
|||
await this.storage.delete(prov);
|
||||
}
|
||||
|
||||
async orgDeleted({ id }: { id: OrgID }): Promise<void> {
|
||||
async orgDeleted({ id }: OrgInfo): Promise<void> {
|
||||
try {
|
||||
const orgProv = await this.storage.get(OrgProvisioning, id);
|
||||
const owner = await this.storage.get(Account, orgProv.owner);
|
||||
await this.storage.delete(new OrgProvisioning({ orgId: id }));
|
||||
const accountProv = await this.storage.get(AccountProvisioning, orgProv.owner);
|
||||
const accountProv = await this.storage.get(AccountProvisioning, await getIdFromEmail(owner.email));
|
||||
accountProv.orgs = accountProv.orgs.filter((id) => id !== orgProv.id);
|
||||
await this.storage.save(accountProv);
|
||||
console.log("org deleted", orgProv, accountProv);
|
||||
} catch (e) {
|
||||
if (e.code !== ErrorCode.NOT_FOUND) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async orgOwnerChanged(
|
||||
{ id }: OrgInfo,
|
||||
prevOwner: { email: string; id: AccountID },
|
||||
newOwner: { email: string; id: AccountID }
|
||||
) {
|
||||
const [orgProv, prevOwnerProv, newOwnerProv] = await Promise.all([
|
||||
this.storage.get(OrgProvisioning, id),
|
||||
this.storage.get(AccountProvisioning, await getIdFromEmail(prevOwner.email)),
|
||||
this.storage.get(AccountProvisioning, await getIdFromEmail(newOwner.email)),
|
||||
]);
|
||||
|
||||
if (newOwnerProv.orgs.length) {
|
||||
throw new Err(
|
||||
ErrorCode.PROVISIONING_NOT_ALLOWED,
|
||||
"You cannot transfer this organization to this account because they're already owner of a different organization."
|
||||
);
|
||||
}
|
||||
|
||||
orgProv.owner = newOwner.id;
|
||||
prevOwnerProv.orgs = prevOwnerProv.orgs.filter((o) => o !== id);
|
||||
newOwnerProv.orgs.push(id);
|
||||
|
||||
await Promise.all([
|
||||
this.storage.save(orgProv),
|
||||
this.storage.save(prevOwnerProv),
|
||||
this.storage.save(newOwnerProv),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -841,7 +841,7 @@ export class Controller extends API {
|
|||
if (org.isOwner(account)) {
|
||||
await this.deleteOrg(org.id);
|
||||
} else {
|
||||
org.removeMember(account);
|
||||
await org.removeMember(account, false);
|
||||
await this.storage.save(org);
|
||||
}
|
||||
}
|
||||
|
@ -927,6 +927,7 @@ export class Controller extends API {
|
|||
invites,
|
||||
revision,
|
||||
minMemberUpdated,
|
||||
owner,
|
||||
}: Org) {
|
||||
const { account, provisioning } = this._requireAuth();
|
||||
|
||||
|
@ -970,10 +971,11 @@ export class Controller extends API {
|
|||
const removedInvites = org.invites.filter(({ id }) => !invites.some((inv) => id === inv.id));
|
||||
const addedGroups = groups.filter((group) => !org.getGroup(group.name));
|
||||
|
||||
// Only org owners can add or remove members, change roles or create invites
|
||||
// Only org owners can add or remove members, change roles, create invites or transfer ownership
|
||||
if (
|
||||
!isOwner &&
|
||||
(addedMembers.length ||
|
||||
(owner !== org.owner ||
|
||||
addedMembers.length ||
|
||||
removedMembers.length ||
|
||||
addedInvites.length ||
|
||||
removedInvites.length ||
|
||||
|
@ -1014,6 +1016,14 @@ export class Controller extends API {
|
|||
vaults,
|
||||
});
|
||||
|
||||
if (org.owner !== owner) {
|
||||
await this.provisioner.orgOwnerChanged(
|
||||
org,
|
||||
org.getMember({ id: org.owner })!,
|
||||
org.getMember({ id: owner })!
|
||||
);
|
||||
}
|
||||
|
||||
// certain properties may only be updated by organization owners
|
||||
if (isOwner) {
|
||||
Object.assign(org, {
|
||||
|
@ -1026,6 +1036,7 @@ export class Controller extends API {
|
|||
accessors,
|
||||
invites,
|
||||
minMemberUpdated,
|
||||
owner,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1193,10 +1204,10 @@ export class Controller extends API {
|
|||
})
|
||||
);
|
||||
|
||||
await this.provisioner.orgDeleted(org);
|
||||
|
||||
await this.storage.delete(org);
|
||||
|
||||
await this.provisioner.orgDeleted(org);
|
||||
|
||||
this.log("org.delete", { org: { name: org.name, id: org.id, owner: org.owner } });
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
Provisioning,
|
||||
} from "@padloc/core/src/provisioning";
|
||||
import { uuid } from "@padloc/core/src/util";
|
||||
import { Org } from "@padloc/core/src/org";
|
||||
import { Org, OrgInfo } from "@padloc/core/src/org";
|
||||
import { createServer, IncomingMessage, ServerResponse } from "http";
|
||||
import { getCryptoProvider } from "@padloc/core/src/platform";
|
||||
import { base64ToBytes, bytesToBase64, stringToBytes } from "@padloc/core/src/encoding";
|
||||
|
@ -186,6 +186,19 @@ export class StripeProvisioner extends BasicProvisioner {
|
|||
await super.accountDeleted(params);
|
||||
}
|
||||
|
||||
async orgDeleted(org: OrgInfo): Promise<void> {
|
||||
await super.orgDeleted(org);
|
||||
|
||||
const account = await this.storage.get(Account, org.owner);
|
||||
const provisioning = await this.getProvisioning(account);
|
||||
const { tier } = this._getSubscriptionInfo(provisioning.account.metaData.customer);
|
||||
|
||||
if ([Tier.Business, Tier.Team, Tier.Family].includes(tier)) {
|
||||
await this._setTier(provisioning, Tier.Premium);
|
||||
await this._syncBilling(provisioning);
|
||||
}
|
||||
}
|
||||
|
||||
async getProvisioning(opts: { email: string; accountId?: string | undefined }) {
|
||||
const provisioning = await super.getProvisioning(opts);
|
||||
if (
|
||||
|
@ -194,12 +207,51 @@ export class StripeProvisioner extends BasicProvisioner {
|
|||
!provisioning.account.metaData?.lastSync ||
|
||||
provisioning.account.metaData.lastSync < Date.now() - this.config.forceSyncAfter * 1000)
|
||||
) {
|
||||
console.log("sync billing!!");
|
||||
await this._syncBilling(provisioning);
|
||||
}
|
||||
return super.getProvisioning(opts);
|
||||
}
|
||||
|
||||
async orgOwnerChanged(
|
||||
org: OrgInfo,
|
||||
prevOwner: { email: string; id: string },
|
||||
newOwner: { email: string; id: string }
|
||||
): Promise<void> {
|
||||
await super.orgOwnerChanged(org, prevOwner, newOwner);
|
||||
|
||||
const [prevOwnerProv, newOwnerProv] = await Promise.all([
|
||||
this.getProvisioning(prevOwner),
|
||||
this.getProvisioning(newOwner),
|
||||
]);
|
||||
|
||||
const { tier } = this._getSubscriptionInfo(prevOwnerProv.account.metaData.customer);
|
||||
|
||||
if ([Tier.Business, Tier.Team, Tier.Family].includes(tier)) {
|
||||
await this._setTier(prevOwnerProv, Tier.Premium);
|
||||
}
|
||||
|
||||
await Promise.all([this._syncBilling(prevOwnerProv), this._syncBilling(newOwnerProv)]);
|
||||
}
|
||||
|
||||
private async _setTier(provisioning: Provisioning, tier: Tier): Promise<void> {
|
||||
const { subscription, price } = this._getSubscriptionInfo(provisioning.account.metaData.customer);
|
||||
|
||||
if (subscription) {
|
||||
const premium = this._getProduct(tier)!;
|
||||
const newPrice = price?.recurring?.interval === "month" ? premium.priceMonthly : premium.priceAnnual;
|
||||
await this._stripe.subscriptions.update(subscription.id, {
|
||||
cancel_at_period_end: false,
|
||||
proration_behavior: "create_prorations",
|
||||
items: [
|
||||
{
|
||||
id: subscription.items.data[0].id,
|
||||
price: newPrice!.id,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _getProduct(tier: Tier) {
|
||||
return [...this._products.values()].find((entry) => entry.tier === tier);
|
||||
}
|
||||
|
@ -265,8 +317,6 @@ export class StripeProvisioner extends BasicProvisioner {
|
|||
// Create a new customer
|
||||
if (!customer || customer.deleted) {
|
||||
const account = accountId ? await this.storage.get(Account, accountId).catch(() => null) : null;
|
||||
console.log("creating customer...", accountId, account?.email, account?.name);
|
||||
console.trace();
|
||||
const testClock = await this._stripe.testHelpers.testClocks.create({
|
||||
name: `Test Clock for ${email}`,
|
||||
frozen_time: Math.floor(Date.now() / 1000),
|
||||
|
@ -1250,7 +1300,6 @@ export class StripeProvisioner extends BasicProvisioner {
|
|||
try {
|
||||
const body = await readBody(httpReq);
|
||||
if (this.config.webhookSecret) {
|
||||
console.log("verifying signature", httpReq.headers["stripe-signature"]);
|
||||
event = this._stripe.webhooks.constructEvent(
|
||||
body,
|
||||
httpReq.headers["stripe-signature"] as string,
|
||||
|
@ -1265,8 +1314,6 @@ export class StripeProvisioner extends BasicProvisioner {
|
|||
return;
|
||||
}
|
||||
|
||||
console.log("handle stripe event", event.type);
|
||||
|
||||
let customer: Stripe.Customer | Stripe.DeletedCustomer | undefined = undefined;
|
||||
|
||||
switch (event.type) {
|
||||
|
@ -1281,13 +1328,6 @@ export class StripeProvisioner extends BasicProvisioner {
|
|||
break;
|
||||
}
|
||||
if (customer && !customer.deleted && customer.email) {
|
||||
console.log(
|
||||
"event received for customer",
|
||||
event.type,
|
||||
customer.id,
|
||||
customer.email,
|
||||
customer.metadata.account
|
||||
);
|
||||
const provisioning = await this.getProvisioning({
|
||||
email: customer.email,
|
||||
accountId: customer.metadata.account,
|
||||
|
|
Loading…
Reference in New Issue