fix payment methods.

This commit is contained in:
Simon Larsen
2022-11-17 22:02:37 +00:00
parent b606c17277
commit bc41bba511
8 changed files with 163 additions and 54 deletions

View File

@@ -42,7 +42,8 @@ export default class URL extends DatabaseProperty {
public constructor(
protocol: Protocol,
hostname: Hostname | string,
route?: Route
route?: Route,
queryString?: string
) {
super();
if (hostname instanceof Hostname) {
@@ -56,6 +57,20 @@ export default class URL extends DatabaseProperty {
if (route) {
this.route = route;
}
if (queryString) {
const keyValues = queryString.split("&");
for (const keyValue of keyValues) {
if (keyValue.split("=")[0] && keyValue.split("=")[1]) {
const key = keyValue.split("=")[0];
const value = keyValue.split("=")[1];
if (key && value) {
this._params[key] = value;
}
}
}
}
}
public isHttps(): boolean {
@@ -118,14 +133,22 @@ export default class URL extends DatabaseProperty {
const hostname: Hostname = new Hostname(url.split('/')[0] || '');
let route: Route | undefined;
let queryString: string | undefined;
if (url.split('/').length > 1) {
const paths: Array<string> = url.split('/');
paths.shift();
route = new Route(paths.join('/'));
route = new Route(paths.join('/').split("?")[0]);
}
return new URL(protocol, hostname, route);
queryString = url.split("?")[1] || '';
return new URL(protocol, hostname, route, queryString);
}
public removeQueryString(): URL {
return URL.fromString(this.toString().split("?")[0] || '');
}
public addRoute(route: Route | string): URL {

View File

@@ -16,4 +16,5 @@ export default interface DatabaseCommonInteractionProps {
tenantId?: ObjectID | undefined;
isRoot?: boolean | undefined;
isMultiTenantRequest?: boolean | undefined;
ignoreHooks?: boolean | undefined;
}

View File

@@ -27,12 +27,12 @@ export default class BaseAPI<
TBaseModel extends BaseModel,
TBaseService extends DatabaseService<BaseModel>
> {
public entityType: { new (): TBaseModel };
public entityType: { new(): TBaseModel };
public router: ExpressRouter;
public service: TBaseService;
public constructor(type: { new (): TBaseModel }, service: TBaseService) {
public constructor(type: { new(): TBaseModel }, service: TBaseService) {
this.entityType = type;
const router: ExpressRouter = Express.getRouter();
@@ -144,8 +144,8 @@ export default class BaseAPI<
this.service = service;
}
public getPermissionsForTenant(req:ExpressRequest): Array<UserPermission> {
public getPermissionsForTenant(req: ExpressRequest): Array<UserPermission> {
const permissions: Array<UserPermission> = [];
const props = this.getDatabaseCommonInteractionProps(req);
@@ -156,7 +156,7 @@ export default class BaseAPI<
return props.userTenantAccessPermission[props.tenantId?.toString() || '']?.permissions || [];
}
return permissions;
return permissions;
}
public getDatabaseCommonInteractionProps(
@@ -206,7 +206,6 @@ export default class BaseAPI<
res: ExpressResponse
): Promise<void> {
console.log("LIST")
await this.onBeforeList(req, res);
const skip: PositiveNumber = req.query['skip']
@@ -281,6 +280,8 @@ export default class BaseAPI<
): Promise<void> {
let query: Query<BaseModel> = {};
await this.onBeforeCount(req, res);
if (req.body) {
query = JSONFunctions.deserialize(
req.body['query']
@@ -305,7 +306,7 @@ export default class BaseAPI<
res: ExpressResponse
): Promise<void> {
const objectId: ObjectID = new ObjectID(req.params['id'] as string);
await this.onBeforeGet(req, res);
let select: Select<BaseModel> = {};
let populate: Populate<BaseModel> = {};
@@ -335,6 +336,7 @@ export default class BaseAPI<
req: ExpressRequest,
res: ExpressResponse
): Promise<void> {
await this.onBeforeDelete(req, res);
const objectId: ObjectID = new ObjectID(req.params['id'] as string);
await this.service.deleteBy({
@@ -351,6 +353,7 @@ export default class BaseAPI<
req: ExpressRequest,
res: ExpressResponse
): Promise<void> {
await this.onBeforeUpdate(req, res);
const objectId: ObjectID = new ObjectID(req.params['id'] as string);
const objectIdString: string = objectId.toString();
const body: JSONObject = req.body;
@@ -378,6 +381,7 @@ export default class BaseAPI<
req: ExpressRequest,
res: ExpressResponse
): Promise<void> {
await this.onBeforeCreate(req, res);
const body: JSONObject = req.body;
const item: TBaseModel = BaseModel.fromJSON<TBaseModel>(
@@ -418,4 +422,38 @@ export default class BaseAPI<
): Promise<any> {
return Promise.resolve(true);
}
protected async onBeforeCreate(
_req: ExpressRequest, _res: ExpressResponse
): Promise<any> {
return Promise.resolve(true);
}
protected async onBeforeGet(
_req: ExpressRequest, _res: ExpressResponse
): Promise<any> {
return Promise.resolve(true);
}
protected async onBeforeUpdate(
_req: ExpressRequest, _res: ExpressResponse
): Promise<any> {
return Promise.resolve(true);
}
protected async onBeforeDelete(
_req: ExpressRequest, _res: ExpressResponse
): Promise<any> {
return Promise.resolve(true);
}
protected async onBeforeCount(
_req: ExpressRequest, _res: ExpressResponse
): Promise<any> {
return Promise.resolve(true);
}
}

View File

@@ -7,6 +7,7 @@ import BadDataException from 'Common/Types/Exception/BadDataException';
import Project from 'Model/Models/Project';
import BillingService from './BillingService';
import DeleteBy from '../Types/Database/DeleteBy';
import LIMIT_MAX from 'Common/Types/Database/LimitMax';
export class Service extends DatabaseService<Model> {
public constructor(postgresDatabase?: PostgresDatabase) {
@@ -15,7 +16,6 @@ export class Service extends DatabaseService<Model> {
protected override async onBeforeFind(findBy: FindBy<Model>): Promise<OnFind<Model>> {
console.log(findBy.props);
if (!findBy.props.tenantId) {
throw new BadDataException("ProjectID not found.")
@@ -25,7 +25,8 @@ export class Service extends DatabaseService<Model> {
id: findBy.props.tenantId!,
props: {
...findBy.props,
isRoot: true
isRoot: true,
ignoreHooks: true
},
select: {
_id: true,
@@ -62,7 +63,8 @@ export class Service extends DatabaseService<Model> {
billingPaymentMethod.last4Digits = paymentMethod.last4Digits;
billingPaymentMethod.isDefault = paymentMethod.isDefault;
billingPaymentMethod.paymentProviderPaymentMethodId = paymentMethod.id;
billingPaymentMethod.paymentProviderCustomerId = project.paymentProviderCustomerId;
await this.create({
data: billingPaymentMethod,
props: {
@@ -75,11 +77,29 @@ export class Service extends DatabaseService<Model> {
return { findBy, carryForward: paymentMethods };
}
protected override onBeforeDelete(deleteBy: DeleteBy<Model>): Promise<OnDelete<Model>> {
protected override async onBeforeDelete(deleteBy: DeleteBy<Model>): Promise<OnDelete<Model>> {
const items = await this.findBy({
query: deleteBy.query,
})
query: deleteBy.query,
select: {
_id: true,
paymentProviderPaymentMethodId: true,
paymentProviderCustomerId: true,
},
skip: 0,
limit: LIMIT_MAX,
props: {
isRoot: true,
ignoreHooks: true
}
});
for (const item of items) {
if (item.paymentProviderPaymentMethodId && item.paymentProviderCustomerId) {
await BillingService.deletePaymentMethod(item.paymentProviderCustomerId, item.paymentProviderPaymentMethodId);
}
}
return { deleteBy, carryForward: null };
}
}

View File

@@ -206,7 +206,7 @@ export class BillingService {
const paymenMethods = await this.getPaymentMethods(customerId);
if (paymenMethods.length === 1) {
throw new BadDataException("There's only one poayment method associated with this account. It cannot be deleted. To delete this payment method please add more payment methods to your account.");
throw new BadDataException("There's only one payment method associated with this account. It cannot be deleted. To delete this payment method please add more payment methods to your account.");
}
await this.stripe.paymentMethods.detach(paymentMethodId);

View File

@@ -465,7 +465,7 @@ class DatabaseService<TBaseModel extends BaseModel> {
}
public async create(createBy: CreateBy<TBaseModel>): Promise<TBaseModel> {
const onCreate: OnCreate<TBaseModel> = await this._onBeforeCreate(
const onCreate: OnCreate<TBaseModel> = createBy.props.ignoreHooks ? { createBy, carryForward: [] } : await this._onBeforeCreate(
createBy
);
@@ -519,13 +519,15 @@ class DatabaseService<TBaseModel extends BaseModel> {
try {
createBy.data = await this.getRepository().save(createBy.data);
createBy.data = await this.onCreateSuccess(
{
createBy,
carryForward,
},
createBy.data
);
if (!createBy.props.ignoreHooks) {
createBy.data = await this.onCreateSuccess(
{
createBy,
carryForward,
},
createBy.data
);
}
return createBy.data;
} catch (error) {
await this.onCreateError(error as Exception);
@@ -653,7 +655,7 @@ class DatabaseService<TBaseModel extends BaseModel> {
);
findBy.query = checkReadPermissionType.query;
let count: number = 0;
let count: number = 0;
if (distinctOn) {
const queryBuilder: SelectQueryBuilder<TBaseModel> = this.getQueryBuilder(this.modelName)
@@ -668,14 +670,14 @@ class DatabaseService<TBaseModel extends BaseModel> {
count = await queryBuilder.getCount();
} else {
count = await this.getRepository().count({
count = await this.getRepository().count({
where: findBy.query as any,
skip: (findBy.skip as PositiveNumber).toNumber(),
take: (findBy.limit as PositiveNumber).toNumber(),
});
}
let countPositive: PositiveNumber = new PositiveNumber(count);
countPositive = await this.onCountSuccess(countPositive);
return countPositive;
@@ -697,7 +699,7 @@ class DatabaseService<TBaseModel extends BaseModel> {
private async _deleteBy(deleteBy: DeleteBy<TBaseModel>): Promise<number> {
try {
const onDelete: OnDelete<TBaseModel> = await this.onBeforeDelete(
const onDelete: OnDelete<TBaseModel> = deleteBy.props.ignoreHooks ? { deleteBy, carryForward: [] } : await this.onBeforeDelete(
deleteBy
);
const beforeDeleteBy: DeleteBy<TBaseModel> = onDelete.deleteBy;
@@ -716,7 +718,7 @@ class DatabaseService<TBaseModel extends BaseModel> {
limit: LIMIT_MAX,
populate: {},
select: {},
props: beforeDeleteBy.props,
props: { ...beforeDeleteBy.props, ignoreHooks: true },
});
await this._updateBy({
@@ -726,19 +728,24 @@ class DatabaseService<TBaseModel extends BaseModel> {
} as any,
props: {
isRoot: true,
ignoreHooks: true
},
});
const numberOfDocsAffected: number =
(await this.getRepository().delete(beforeDeleteBy.query as any))
.affected || 0;
if (!deleteBy.props.ignoreHooks) {
await this.onDeleteSuccess(
{ deleteBy, carryForward },
items.map((i: TBaseModel) => {
return new ObjectID(i._id!);
})
);
await this.onDeleteSuccess(
{ deleteBy, carryForward },
items.map((i: TBaseModel) => {
return new ObjectID(i._id!);
})
);
}
return numberOfDocsAffected;
} catch (error) {
@@ -762,7 +769,7 @@ class DatabaseService<TBaseModel extends BaseModel> {
createdAt: SortOrder.Descending,
};
}
const onFind: OnFind<TBaseModel> = await this.onBeforeFind(findBy);
const onFind: OnFind<TBaseModel> = findBy.props.ignoreHooks ? { findBy, carryForward: [] } : await this.onBeforeFind(findBy);
const onBeforeFind: FindBy<TBaseModel> = onFind.findBy;
const carryForward: any = onFind.carryForward;
@@ -824,13 +831,14 @@ class DatabaseService<TBaseModel extends BaseModel> {
decryptedItems,
onBeforeFind
);
decryptedItems = await (
await this.onFindSuccess(
{ findBy, carryForward },
decryptedItems
)
).carryForward;
if (!findBy.props.ignoreHooks) {
decryptedItems = await (
await this.onFindSuccess(
{ findBy, carryForward },
decryptedItems
)
).carryForward;
}
return decryptedItems;
} catch (error) {
@@ -934,7 +942,7 @@ class DatabaseService<TBaseModel extends BaseModel> {
private async _updateBy(updateBy: UpdateBy<TBaseModel>): Promise<number> {
try {
const onUpdate: OnUpdate<TBaseModel> = await this.onBeforeUpdate(
const onUpdate: OnUpdate<TBaseModel> = updateBy.props.ignoreHooks ? { updateBy, carryForward: [] } : await this.onBeforeUpdate(
updateBy
);
@@ -961,7 +969,7 @@ class DatabaseService<TBaseModel extends BaseModel> {
limit: LIMIT_MAX,
populate: {},
select: {},
props: beforeUpdateBy.props,
props: { ...beforeUpdateBy.props, ignoreHooks: true },
});
for (let item of items) {
@@ -984,12 +992,14 @@ class DatabaseService<TBaseModel extends BaseModel> {
// )
// ).affected || 0;
await this.onUpdateSuccess(
{ updateBy, carryForward },
items.map((i: TBaseModel) => {
return new ObjectID(i._id!);
})
);
if (!updateBy.props.ignoreHooks) {
await this.onUpdateSuccess(
{ updateBy, carryForward },
items.map((i: TBaseModel) => {
return new ObjectID(i._id!);
})
);
}
return items.length;
} catch (error) {

View File

@@ -24,12 +24,14 @@ const CheckoutForm = (props: ComponentProps) => {
// Make sure to disable form submission until Stripe.js has loaded.
return;
}
console.log(Navigation.getCurrentURL().removeQueryString().toString());
const { error } = await stripe.confirmSetup({
//`Elements` instance that was used to create the Payment Element
elements,
confirmParams: {
return_url: Navigation.getCurrentURL().toString(),
return_url: Navigation.getCurrentURL().removeQueryString().toString(),
},
});

View File

@@ -175,6 +175,21 @@ export default class BillingPaymentMethod extends BaseModel {
public paymentProviderPaymentMethodId?: string = undefined;
@ColumnAccessControl({
create: [],
read: [Permission.ProjectOwner],
update: [],
})
@TableColumn({ type: TableColumnType.ShortText })
@Column({
type: ColumnType.ShortText,
length: ColumnLength.ShortText,
nullable: false,
unique: false,
})
public paymentProviderCustomerId?: string = undefined;
@ColumnAccessControl({
create: [Permission.ProjectOwner],
read: [Permission.ProjectOwner],