Skip to content

Commit 3039cbb

Browse files
committed
Add composePayment query
1 parent 81dcb5f commit 3039cbb

File tree

2 files changed

+172
-0
lines changed

2 files changed

+172
-0
lines changed

src/resolvers/billingNew.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@ import cloudPaymentsApi, { CloudPaymentsJsonData } from '../utils/cloudPaymentsA
1717
*/
1818
const AMOUNT_FOR_CARD_VALIDATION = 1;
1919

20+
/**
21+
* Input data for composePayment query
22+
*/
23+
interface ComposePaymentArgs {
24+
input: {
25+
workspaceId: string;
26+
tariffPlanId: string;
27+
shouldSaveCard?: boolean;
28+
};
29+
}
30+
2031
/**
2132
* Data for processing payment with saved card
2233
*/
@@ -58,6 +69,86 @@ export default {
5869
): Promise<BusinessOperationModel[]> {
5970
return factories.businessOperationsFactory.getWorkspacesBusinessOperations(ids);
6071
},
72+
73+
/**
74+
* GraphQL version of composePayment: prepares data before charge
75+
*/
76+
async composePayment(
77+
_obj: undefined,
78+
{ input }: ComposePaymentArgs,
79+
{ user, factories }: ResolverContextWithUser
80+
): Promise<{
81+
invoiceId: string;
82+
plan: { id: string; name: string; monthlyCharge: number };
83+
isCardLinkOperation: boolean;
84+
currency: string;
85+
checksum: string;
86+
nextPaymentDate: Date;
87+
}> {
88+
const { workspaceId, tariffPlanId, shouldSaveCard } = input;
89+
90+
if (!workspaceId || !tariffPlanId || !user?.id) {
91+
throw new UserInputError('No workspaceId, tariffPlanId or user id provided');
92+
}
93+
94+
const workspace = await factories.workspacesFactory.findById(workspaceId);
95+
const plan = await factories.plansFactory.findById(tariffPlanId);
96+
97+
if (!workspace || !plan) {
98+
throw new UserInputError("Can't get workspace or plan by provided ids");
99+
}
100+
101+
const member = await workspace.getMemberInfo(user.id);
102+
103+
if (!member) {
104+
throw new UserInputError('User is not a member of the workspace');
105+
}
106+
107+
const now = new Date();
108+
const invoiceId = `${workspace.name} ${now.getDate()}/${now.getMonth() + 1} ${plan.name}`;
109+
110+
const isCardLinkOperation = workspace.tariffPlanId.toString() === tariffPlanId && !workspace.isTariffPlanExpired();
111+
112+
// Calculate next payment date
113+
const lastChargeDate = workspace.lastChargeDate ? new Date(workspace.lastChargeDate) : now;
114+
let nextPaymentDate = isCardLinkOperation ? new Date(lastChargeDate) : new Date(now);
115+
116+
if (workspace.isDebug) {
117+
nextPaymentDate.setDate(nextPaymentDate.getDate() + 1);
118+
} else {
119+
nextPaymentDate.setMonth(nextPaymentDate.getMonth() + 1);
120+
}
121+
122+
const checksumData = isCardLinkOperation
123+
? {
124+
isCardLinkOperation: true as const,
125+
workspaceId: workspace._id.toString(),
126+
userId: user.id,
127+
nextPaymentDate: nextPaymentDate.toISOString(),
128+
}
129+
: {
130+
workspaceId: workspace._id.toString(),
131+
userId: user.id,
132+
tariffPlanId: plan._id.toString(),
133+
shouldSaveCard: Boolean(shouldSaveCard),
134+
nextPaymentDate: nextPaymentDate.toISOString(),
135+
};
136+
137+
const checksum = await checksumService.generateChecksum(checksumData);
138+
139+
return {
140+
invoiceId,
141+
plan: {
142+
id: plan._id.toString(),
143+
name: plan.name,
144+
monthlyCharge: plan.monthlyCharge,
145+
},
146+
isCardLinkOperation,
147+
currency: 'RUB',
148+
checksum,
149+
nextPaymentDate,
150+
};
151+
},
61152
},
62153
/**
63154
* Resolver for Union Payload type.

src/typeDefs/billing.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,26 @@ type BillingSession {
3434
paymentURL: String! @renameFrom(name: "PaymentURL")
3535
}
3636
37+
"""
38+
Minimal plan info used in composePayment response
39+
"""
40+
type ComposePaymentPlanInfo {
41+
"""
42+
Plan id in MongoDB
43+
"""
44+
id: ID!
45+
46+
"""
47+
Plan name
48+
"""
49+
name: String!
50+
51+
"""
52+
Monthly charge for plan
53+
"""
54+
monthlyCharge: Int!
55+
}
56+
3757
"""
3858
User bank card
3959
"""
@@ -197,11 +217,72 @@ input PayOnceInput {
197217
}
198218
199219
220+
"""
221+
Input for composePayment query
222+
"""
223+
input ComposePaymentInput {
224+
"""
225+
Workspace id for which the payment will be made
226+
"""
227+
workspaceId: ID!
228+
229+
"""
230+
Tariff plan id user is going to pay for
231+
"""
232+
tariffPlanId: ID!
233+
234+
"""
235+
Whether card should be saved for future recurrent payments
236+
"""
237+
shouldSaveCard: Boolean
238+
}
239+
240+
"""
241+
Response of composePayment query
242+
"""
243+
type ComposePaymentResponse {
244+
"""
245+
Human-readable invoice identifier
246+
"""
247+
invoiceId: String!
248+
249+
"""
250+
Selected plan info
251+
"""
252+
plan: ComposePaymentPlanInfo!
253+
254+
"""
255+
True if only card linking validation payment is expected
256+
"""
257+
isCardLinkOperation: Boolean!
258+
259+
"""
260+
Currency code
261+
"""
262+
currency: String!
263+
264+
"""
265+
Checksum for subsequent payment verification
266+
"""
267+
checksum: String!
268+
269+
"""
270+
Next payment date (recurrent start)
271+
"""
272+
nextPaymentDate: DateTime!
273+
}
274+
275+
200276
extend type Query {
201277
"""
202278
Get workspace billing history
203279
"""
204280
businessOperations("Workspaces IDs" ids: [ID!] = []): [BusinessOperation!]! @requireAuth @requireAdmin
281+
282+
"""
283+
Prepare payment data before charge (GraphQL version of composePayment)
284+
"""
285+
composePayment(input: ComposePaymentInput!): ComposePaymentResponse! @requireAuth
205286
}
206287
207288
"""

0 commit comments

Comments
 (0)