Skip to content

Commit

Permalink
#23 & #28 | fix subscription
Browse files Browse the repository at this point in the history
  • Loading branch information
leophan07 committed Dec 4, 2020
1 parent ce2eced commit 6018047
Show file tree
Hide file tree
Showing 26 changed files with 507 additions and 135 deletions.
26 changes: 26 additions & 0 deletions api/email-templates/invoicePaymentFailed.mjml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<mjml>
<mj-body background-color="#f1f1f1">
<mj-section>
<mj-column>
<mj-image
width="100px"
src="https://jslancer.com/assets/images/logo/jslancer.png"
></mj-image>
</mj-column>
</mj-section>
<mj-section background-color="#fff">
<mj-column>
<mj-text font-size="30px" align="center">
Invoice payment failed
</mj-text>
<mj-divider border-color="#06adef" width="100px"></mj-divider>
<mj-text font-size="18px" align="left" color="#555" line-height="30px">
Hi {{name}}! Your invoice payment failed. Your plan expire on {{date}}.
</mj-text>
<mj-text font-size="18px" align="left" color="#555" line-height="30px">
Please check your card and payment again to extend your plan.
</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
26 changes: 26 additions & 0 deletions api/email-templates/invoicePaymentSuccess.mjml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<mjml>
<mj-body background-color="#f1f1f1">
<mj-section>
<mj-column>
<mj-image
width="100px"
src="https://jslancer.com/assets/images/logo/jslancer.png"
></mj-image>
</mj-column>
</mj-section>
<mj-section background-color="#fff">
<mj-column>
<mj-text font-size="30px" align="center">
Invoice payment successfully
</mj-text>
<mj-divider border-color="#06adef" width="100px"></mj-divider>
<mj-text font-size="18px" align="left" color="#555" line-height="30px">
Hi {{name}}! Your invoice payment successfully. Your plan expire on {{date}}.
</mj-text>
<mj-text font-size="18px" align="left" color="#555" line-height="30px">
You can check your invoice at this <a href="{{link}}">link</a>.
</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
26 changes: 26 additions & 0 deletions api/email-templates/trialWillEnd.mjml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<mjml>
<mj-body background-color="#f1f1f1">
<mj-section>
<mj-column>
<mj-image
width="100px"
src="https://jslancer.com/assets/images/logo/jslancer.png"
></mj-image>
</mj-column>
</mj-section>
<mj-section background-color="#fff">
<mj-column>
<mj-text font-size="30px" align="center">
Trial will end
</mj-text>
<mj-divider border-color="#06adef" width="100px"></mj-divider>
<mj-text font-size="18px" align="left" color="#555" line-height="30px">
Hi {{name}}! Your trial will expire on {{date}}. We will automatically charge your money when the trial ends.
</mj-text>
<mj-text font-size="18px" align="left" color="#555" line-height="30px">
Please go to your plan detail if you want to change or unsubscribe subscription.
</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
11 changes: 0 additions & 11 deletions api/graphql/resolvers/stripe.resolver.js

This file was deleted.

3 changes: 1 addition & 2 deletions api/graphql/root.resolver.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import userResolves from './resolvers/user.resolver';
import userPlanResolves from './resolvers/user-plan.resolver';
import stripeResolves from './resolvers/stripe.resolver';

export default [userResolves, userPlanResolves, stripeResolves];
export default [userResolves, userPlanResolves];
3 changes: 1 addition & 2 deletions api/graphql/root.schema.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Apollo from 'apollo-server-express';
import { UserSchema } from './schemas/user.schema';
import { UserPlanSchema } from './schemas/user-plan.schema';
import { StripeSchema } from './schemas/stripe.schema';

const { gql } = Apollo;
const rootSchema = gql`
Expand All @@ -21,4 +20,4 @@ const rootSchema = gql`
}
`;

export default [rootSchema, UserSchema, UserPlanSchema, StripeSchema];
export default [rootSchema, UserSchema, UserPlanSchema];
9 changes: 0 additions & 9 deletions api/graphql/schemas/stripe.schema.js

This file was deleted.

2 changes: 2 additions & 0 deletions api/graphql/schemas/user-plan.schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export const UserPlanSchema = gql`
amount: Float!
productType: String!
priceType: String!
expiredAt: Date
deletedAt: Date
}
extend type Query {
Expand Down
9 changes: 9 additions & 0 deletions api/jobs/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function main() {
Promise.all([
// todo
]).then(() => {
process.exit(0);
}).catch(() => process.exit(0));
}

main();
5 changes: 5 additions & 0 deletions api/migrations/20201126173501_create-user-plans.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ export function up(knex) {
t.integer('product_id').unsigned().notNullable();
t.integer('price_id').unsigned().notNullable();
t.string('subcription_id').notNullable();
t.string('customer_id').notNullable();
t.boolean('is_trial').defaultTo(false);
t.dateTime('expired_at');
t.boolean('is_active').defaultTo(true);
t.dateTime('created_at')
.notNullable()
.defaultTo(knex.raw('CURRENT_TIMESTAMP'));
t.dateTime('updated_at')
.notNullable()
.defaultTo(knex.raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'));
t.dateTime('deleted_at');
t.foreign('user_id').references('id').inTable('users');
t.foreign('product_id').references('id').inTable('products');
t.foreign('price_id').references('id').inTable('prices');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ export function up(knex) {
t.increments('id');
t.integer('user_id').unsigned().notNullable();
t.string('permission');
t.integer('user_plan_id').unsigned();
t.dateTime('created_at')
.notNullable()
.defaultTo(knex.raw('CURRENT_TIMESTAMP'));
t.dateTime('updated_at')
.notNullable()
.defaultTo(knex.raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'));
t.dateTime('deleted_at');
t.foreign('user_id').references('id').inTable('users');
t.foreign('user_plan_id').references('id').inTable('user_plans');
});
}

Expand Down
1 change: 1 addition & 0 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"apollo-server-express": "^2.19.0",
"axios": "^0.21.0",
"bcryptjs": "^2.4.3",
"body-parser": "^1.19.0",
"bunyan": "^1.8.14",
"cors": "^2.8.5",
"crypto": "^1.0.1",
Expand Down
15 changes: 13 additions & 2 deletions api/repository/user.repository.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import database from '~/config/database.config';
import { userTokenColumns } from './user_tokens.repository';
import { TABLES } from '~/constants/table-name.constant';
import { insertUserPlan } from './user_plans.repository';
import { insertMultiPermission } from './user_permission.repository';
import { PERMISSION_PLAN } from '~/constants/billing.constant';

const TABLE = TABLES.users;

Expand Down Expand Up @@ -30,20 +32,29 @@ export async function findUser({ id, email, provider_id, provider, deleted_at =
return database(TABLE).where(condition).first();
}

export async function createUser(userData, userPlanData = null) {
export async function createUser(userData, userPlanData = null, planType = null) {
let t;
try {
t = await database.transaction();
const [userId] = await database(TABLE).transacting(t).insert(userData);

if (userPlanData) {
await insertUserPlan(
const [userPlanId] = await insertUserPlan(
{
...userPlanData,
user_id: userId,
},
t,
);

if (planType && PERMISSION_PLAN[planType]) {
const userPermissionData = PERMISSION_PLAN[planType].map((permission) => ({
user_id: userId,
user_plan_id: userPlanId,
permission,
}));
await insertMultiPermission(userPermissionData, t);
}
}

await t.commit();
Expand Down
20 changes: 18 additions & 2 deletions api/repository/user_permission.repository.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
import database from '~/config/database.config';
import { TABLES } from '~/constants/table-name.constant';
import formatDateDB from '~/utils/format-date-db';

const TABLE = TABLES.userPermissions;

export const userPermissionColumns = {
id: 'user_permissions.id',
userId: 'user_permissions.user_id',
userPlanId: 'user_permissions.user_plan_id',
permission: 'user_permissions.permission',
createAt: 'user_permissions.created_at',
updatedAt: 'user_permissions.updated_at',
deletedAt: 'user_permissions.deleted_at',
};

export async function insertMultiPermission(data) {
return database(TABLE).insert(data);
export async function insertMultiPermission(data, transaction = null) {
const query = database(TABLE).insert(data);
if (!transaction) {
return query;
}
return query.transacting(transaction);
}

export function deletePermissionByUserPlanId(userPlanId, dateDeleted = null) {
const deleteAt = dateDeleted || formatDateDB();
return database(TABLE).where({ [userPermissionColumns.userPlanId]: userPlanId }).update({ deleted_at: deleteAt });
}

export function deletePermissionByUserPlanIds(userPlanIds) {
return database(TABLE).whereIn({ [userPermissionColumns.userPlanId]: userPlanIds }).update({ deleted_at: formatDateDB() });
}
30 changes: 27 additions & 3 deletions api/repository/user_plans.repository.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import database from '~/config/database.config';
import { TABLES } from '~/constants/table-name.constant';
import formatDateDB from '~/utils/format-date-db';
import { priceColumns } from './prices.repository';
import { productColumns } from './products.repository';

Expand All @@ -11,8 +12,13 @@ export const userPlanColumns = {
productId: 'user_plans.product_id',
priceId: 'user_plans.price_id',
subcriptionId: 'user_plans.subcription_id',
customerId: 'user_plans.customer_id',
isTrial: 'user_plans.is_trial',
expiredAt: 'user_plans.expired_at',
isActive: 'user_plans.is_active',
createAt: 'user_plans.created_at',
updatedAt: 'user_plans.updated_at',
deletedAt: 'user_plans.deleted_at',
};

export function insertUserPlan(data, transaction = null) {
Expand All @@ -24,7 +30,24 @@ export function insertUserPlan(data, transaction = null) {
}

export function getUserPlanById(id) {
return database(TABLE).where({ id }).first();
return database(TABLE)
.where({ id, [userPlanColumns.isActive]: true })
.where(userPlanColumns.expiredAt, '>=', formatDateDB())
.first();
}

export function getUserPlanExpired() {
return database(TABLE)
.whereNotNull([userPlanColumns.deletedAt])
.where(userPlanColumns.expiredAt, '<', formatDateDB());
}

export function getUserPlanByCustomerId(customerId) {
return database(TABLE)
.leftJoin(TABLES.prices, userPlanColumns.priceId, priceColumns.id)
.select(userPlanColumns, `${priceColumns.type} as priceType`)
.where({ [userPlanColumns.customerId]: customerId, [userPlanColumns.isActive]: true })
.first();
}

export function updateUserPlanById(id, data) {
Expand All @@ -36,10 +59,11 @@ export function getUserPlanByUserId(userId) {
.leftJoin(TABLES.products, userPlanColumns.productId, productColumns.id)
.leftJoin(TABLES.prices, userPlanColumns.priceId, priceColumns.id)
.select(userPlanColumns, productColumns.name, `${productColumns.type} as productType`, priceColumns.amount, `${priceColumns.type} as priceType`)
.where({ user_id: userId })
.where({ [userPlanColumns.userId]: userId, [userPlanColumns.isActive]: true })
.where(userPlanColumns.expiredAt, '>=', formatDateDB())
.first();
}

export function deleteUserPlanById(id) {
return database(TABLE).where({ id }).del();
return database(TABLE).where({ id }).update({ [userPlanColumns.deletedAt]: formatDateDB() });
}
Loading

0 comments on commit 6018047

Please sign in to comment.