Skip to content

Commit 83c8a09

Browse files
feat(payment-service): add subscription payments (#360)
* feat(payment-service): add subscription payments * refactor(payment-service): sonar fixes * refactor(payment-service): fix build fail * refactor(payment-service): lint fix * refactor(payment-service): update migration and fix errors * refactor(payment-service): refractor subscription api as per Rest format * refactor(payment-service): resolved PR comments and sonar fixes * feat(payment-service): add authentication and authorization in subscription controller * refactor(payment-service): fix sonar smells * feat(payment-service): add authentication and authorization in sub-tran controller * refactor(payment-service): sonar fixes * refactor(payment-service): sonar fix * refactor(payment-service): resolved merge conflicts * refactor(payment-service): sonar fixes * refactor(payment-service): merge conflicts in package.json * refactor(payment-service): resolved conflicts in package-lock.json * refactor(payment-service): resolved merge conflicts in pacakge-lock file * refactor(payment-service): reslove fail cause of npm audit check Co-authored-by: akshatdubeysf <77672713+akshatdubeysf@users.noreply.github.com>
1 parent b45a86d commit 83c8a09

26 files changed

Lines changed: 901 additions & 35 deletions
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
11
module.exports = {
22
extends: '@loopback/eslint-config',
3+
rules: {
4+
'no-extra-boolean-cast': 'off',
5+
'@typescript-eslint/interface-name-prefix': 'off',
6+
'no-prototype-builtins': 'off',
7+
},
8+
parserOptions: {
9+
project: './tsconfig.json',
10+
tsconfigRootDir: __dirname,
11+
},
312
};

services/payment-service/migrations/sqls/20210722063857-init-up.sql

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ CREATE TABLE main.templates (
1313
payment_gateway_id uuid NOT NULL ,
1414
name varchar NOT NULL ,
1515
"template" text NOT NULL ,
16+
type varchar NOT NULL ,
1617
CONSTRAINT templates_pkey PRIMARY KEY ( id )
1718
);
1819

@@ -39,6 +40,21 @@ CREATE TABLE main.transactions (
3940
CONSTRAINT pk_transactions_id PRIMARY KEY ( id )
4041
);
4142

43+
CREATE TABLE main.subscriptions (
44+
id uuid NOT NULL ,
45+
currency varchar ,
46+
status varchar ,
47+
payment_gateway_id uuid ,
48+
payment_method varchar ,
49+
metadata json ,
50+
start_date date ,
51+
end_date date ,
52+
total_amount numeric ,
53+
gateway_subscription_id varchar ,
54+
plan_id varchar ,
55+
CONSTRAINT subscriptions_pkey PRIMARY KEY ( id )
56+
);
57+
4258
ALTER TABLE main.orders ADD CONSTRAINT fk_orders_payment_gateways FOREIGN KEY ( paymentgatewayid ) REFERENCES main.paymentgateways( id );
4359

4460
ALTER TABLE main.templates ADD CONSTRAINT fk_templates_paymentgateways FOREIGN KEY ( payment_gateway_id ) REFERENCES main.paymentgateways( id );

services/payment-service/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
"@loopback/build": "^8.0.0",
8484
"@loopback/eslint-config": "^12.0.0",
8585
"@loopback/testlab": "^4.0.0",
86+
"@types/lodash": "^4.14.169",
8687
"@types/node": "^10.17.60",
8788
"@types/uuid": "^8.3.3",
8889
"db-migrate": "^0.11.12",

services/payment-service/src/component.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,28 @@ import {
1717
DEFAULT_PAYMENT_SERVICE_OPTIONS,
1818
PaymentServiceComponentOptions,
1919
} from './types';
20-
import {Orders, Transactions, PaymentGateways, Templates} from './models';
20+
import {
21+
Orders,
22+
Transactions,
23+
PaymentGateways,
24+
Templates,
25+
Subscriptions,
26+
} from './models';
2127
import {
2228
OrdersController,
2329
TransactionsController,
2430
PaymentGatewaysController,
2531
TemplatesController,
32+
SubscriptionsController,
33+
SubscriptionTransactionsController,
34+
TransactionSubscriptionsController,
2635
} from './controllers';
2736
import {
2837
OrdersRepository,
2938
TransactionsRepository,
3039
PaymentGatewaysRepository,
3140
TemplatesRepository,
41+
SubscriptionsRepository,
3242
} from './repositories';
3343
import {RazorpayProvider, GatewayProvider, StripeProvider} from './providers';
3444

@@ -59,18 +69,28 @@ export class PaymentServiceComponent implements Component {
5969
) {
6070
this.bindings = [];
6171
this.application.component(CoreComponent);
62-
this.models = [Orders, Transactions, PaymentGateways, Templates];
72+
this.models = [
73+
Orders,
74+
Transactions,
75+
PaymentGateways,
76+
Templates,
77+
Subscriptions,
78+
];
6379
this.controllers = [
6480
OrdersController,
6581
TransactionsController,
6682
PaymentGatewaysController,
6783
TemplatesController,
84+
SubscriptionsController,
85+
SubscriptionTransactionsController,
86+
TransactionSubscriptionsController,
6887
];
6988
this.repositories = [
7089
OrdersRepository,
7190
TransactionsRepository,
7291
PaymentGatewaysRepository,
7392
TemplatesRepository,
93+
SubscriptionsRepository,
7494
];
7595
this.providers = {
7696
RazorpayProvider,

services/payment-service/src/controllers/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ export * from './orders.controller';
33
export * from './transactions.controller';
44
export * from './payment-gateways.controller';
55
export * from './templates.controller';
6+
export * from './subscriptions.controller';
7+
export * from './transactions-subscriptions.controller';
8+
export * from './subscriptions-transactions.controller';
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import {inject} from '@loopback/core';
2+
import {repository, DataObject} from '@loopback/repository';
3+
import {
4+
post,
5+
Request,
6+
requestBody,
7+
response,
8+
Response,
9+
RestBindings,
10+
} from '@loopback/rest';
11+
import {v4 as uuidv4} from 'uuid';
12+
import {Subscriptions} from '../models';
13+
import {GatewayBindings, IGateway} from '../providers';
14+
import {SubscriptionsRepository} from '../repositories';
15+
import {authenticate, STRATEGY} from 'loopback4-authentication';
16+
import {authorize} from 'loopback4-authorization';
17+
import {PermissionKey} from '../enums/permission-key.enum';
18+
import {STATUS_CODE} from '@sourceloop/core';
19+
import {ResponseMessage} from '../enums';
20+
const dotenvExt = require('dotenv-extended');
21+
const path = require('path');
22+
dotenvExt.load({
23+
path: path.join(process.env.INIT_CWD, '.env'),
24+
defaults: path.join(process.env.INIT_CWD, '.env.defaults'),
25+
errorOnMissing: false,
26+
includeProcessEnv: true,
27+
});
28+
const redirectStatusCode = 302;
29+
const permRedirectStatusCode = 302;
30+
31+
export class SubscriptionTransactionsController {
32+
constructor(
33+
@inject(RestBindings.Http.RESPONSE) private readonly res: Response,
34+
@inject(RestBindings.Http.REQUEST) private readonly req: Request,
35+
@inject(GatewayBindings.GatewayHelper)
36+
private readonly gatewayHelper: IGateway,
37+
@repository(SubscriptionsRepository)
38+
private readonly subscriptionRepository: SubscriptionsRepository,
39+
) {}
40+
41+
@authenticate(STRATEGY.BEARER)
42+
@authorize({permissions: [PermissionKey.CreateSubscription]})
43+
@post('/create-subscription-and-pay')
44+
@response(redirectStatusCode, {
45+
description: 'Subscription model instance',
46+
content: {
47+
'text/html': {},
48+
},
49+
})
50+
async subscriptionandtransactionscreate(
51+
@requestBody({
52+
content: {
53+
'application/json': {
54+
schema: {
55+
type: 'object',
56+
},
57+
},
58+
},
59+
})
60+
subscriptions: Subscriptions,
61+
): Promise<void> {
62+
const subscriptionEntity = {
63+
id: uuidv4(),
64+
totalAmount: subscriptions?.totalAmount,
65+
status: subscriptions?.status,
66+
metaData: subscriptions?.metaData ?? {},
67+
paymentGatewayId: subscriptions?.paymentGatewayId,
68+
paymentMethod: subscriptions?.paymentmethod,
69+
currency: subscriptions?.currency,
70+
startDate: subscriptions.startDate
71+
? new Date(subscriptions.startDate)
72+
: new Date(),
73+
endDate: subscriptions.endDate
74+
? new Date(subscriptions.endDate)
75+
: new Date(),
76+
planId: subscriptions?.planId,
77+
};
78+
const newSubscription = await this.subscriptionRepository.create(
79+
subscriptionEntity,
80+
);
81+
const hostUrl = this.req.get('host');
82+
const hostProtocol = this.req.protocol;
83+
this.req.query.method = newSubscription.paymentMethod;
84+
return this.res.redirect(
85+
redirectStatusCode,
86+
`${hostProtocol}://${hostUrl}/transactions/subscription/${newSubscription.id}?method=${newSubscription.paymentMethod}`,
87+
);
88+
}
89+
90+
@post('/subscription/transaction/charge')
91+
@response(STATUS_CODE.OK, {
92+
description: 'Subscription model instance',
93+
content: {
94+
'application/json': {},
95+
},
96+
})
97+
async subscriptionTransactionscharge(
98+
@requestBody({
99+
content: {
100+
'application/x-www-form-urlencoded': {
101+
schema: {
102+
type: 'object',
103+
},
104+
},
105+
},
106+
})
107+
chargeResponse: DataObject<{}>,
108+
): Promise<unknown> {
109+
chargeResponse = {
110+
...chargeResponse,
111+
subscriptionId: this.req.query.subscriptionId,
112+
};
113+
const chargeHelper = await this.gatewayHelper.subscriptionCharge(
114+
chargeResponse,
115+
);
116+
const hostUrl = this.req.get('host');
117+
const hostProtocol = this.req.protocol;
118+
const defaultUrl = `${hostProtocol}://${hostUrl}`;
119+
if (chargeHelper?.res === ResponseMessage.Sucess) {
120+
return this.res.redirect(
121+
permRedirectStatusCode,
122+
`${process.env.SUCCESS_CALLBACK_URL ?? defaultUrl}`,
123+
);
124+
} else {
125+
return this.res.redirect(
126+
permRedirectStatusCode,
127+
`${process.env.FAILURE_CALLBACK_URL ?? defaultUrl}`,
128+
);
129+
}
130+
}
131+
}

0 commit comments

Comments
 (0)