@@ -43,11 +43,12 @@ import { PaymentData } from './types/paymentData';
4343import cloudPaymentsApi from '../utils/cloudPaymentsApi' ;
4444import PlanModel from '../models/plan' ;
4545import { ClientApi , ClientService , CustomerReceiptItem , ReceiptApi , ReceiptTypes , TaxationSystem } from 'cloudpayments' ;
46+ import { ComposePaymentPayload } from './types/composePaymentPayload' ;
4647
47- /**
48- * Custom data of the plan prolongation request
49- */
50- type PlanProlongationData = PlanProlongationPayload & PaymentData ;
48+ interface ComposePaymentRequest extends express . Request {
49+ query : ComposePaymentPayload & { [ key : string ] : any } ;
50+ context : import ( '../types/graphql' ) . ResolverContextBase ;
51+ } ;
5152
5253/**
5354 * Class for describing the logic of payment routes
@@ -99,8 +100,8 @@ export default class CloudPaymentsWebhooks {
99100 * @param req — Express request object
100101 * @param res - Express response object
101102 */
102- private async composePayment ( req : express . Request , res : express . Response ) : Promise < void > {
103- const { workspaceId, tariffPlanId, shouldSaveCard } = req . query as Record < string , string > ;
103+ private async composePayment ( req : ComposePaymentRequest , res : express . Response ) : Promise < void > {
104+ const { workspaceId, tariffPlanId, shouldSaveCard } = req . query ;
104105 const userId = req . context . user . id ;
105106
106107 if ( ! workspaceId || ! tariffPlanId || ! userId ) {
@@ -134,15 +135,23 @@ export default class CloudPaymentsWebhooks {
134135 }
135136 const invoiceId = this . generateInvoiceId ( tariffPlan , workspace ) ;
136137
138+ const isCardLinkOperation = workspace . tariffPlanId . toString ( ) === tariffPlanId && ! this . isPlanExpired ( workspace ) ;
139+
137140 let checksum ;
138141
139142 try {
140- checksum = await checksumService . generateChecksum ( {
143+ const checksumData = isCardLinkOperation ? {
144+ isCardLinkOperation : true ,
145+ workspaceId : workspace . _id . toString ( ) ,
146+ userId : userId ,
147+ } : {
141148 workspaceId : workspace . _id . toString ( ) ,
142149 userId : userId ,
143150 tariffPlanId : tariffPlan . _id . toString ( ) ,
144151 shouldSaveCard : shouldSaveCard === 'true' ,
145- } ) ;
152+ } ;
153+
154+ checksum = await checksumService . generateChecksum ( checksumData ) ;
146155 } catch ( e ) {
147156 const error = e as Error ;
148157
@@ -158,11 +167,32 @@ export default class CloudPaymentsWebhooks {
158167 name : tariffPlan . name ,
159168 monthlyCharge : tariffPlan . monthlyCharge ,
160169 } ,
170+ isCardLinkOperation,
161171 currency : 'RUB' ,
162172 checksum,
163173 } ) ;
164174 }
165175
176+ /**
177+ * Returns true if workspace's plan is expired
178+ * @param workspace - workspace to check
179+ */
180+ private isPlanExpired ( workspace : WorkspaceModel ) : boolean {
181+ const lastChargeDate = new Date ( workspace . lastChargeDate ) ;
182+
183+ let planExpiracyDate ;
184+
185+ if ( workspace . isDebug ) {
186+ planExpiracyDate = lastChargeDate . setDate ( lastChargeDate . getDate ( ) + 1 ) ;
187+ } else {
188+ planExpiracyDate = lastChargeDate . setMonth ( lastChargeDate . getMonth ( ) + 1 ) ;
189+ }
190+
191+ const isPlanExpired = planExpiracyDate < Date . now ( ) ;
192+
193+ return isPlanExpired ;
194+ }
195+
166196 /**
167197 * Generates invoice id for payment
168198 *
@@ -201,7 +231,13 @@ export default class CloudPaymentsWebhooks {
201231 let member : ConfirmedMemberDBScheme ;
202232 let plan : PlanDBScheme ;
203233
204- if ( ! data . workspaceId || ! data . tariffPlanId || ! data . userId ) {
234+ if ( data . isCardLinkOperation && ( ! data . userId || ! data . workspaceId ) ) {
235+ this . sendError ( res , CheckCodes . PAYMENT_COULD_NOT_BE_ACCEPTED , '[Billing / Check] Card linking – invalid data' , body ) ;
236+
237+ return ;
238+ }
239+
240+ if ( ! data . isCardLinkOperation && ( ! data . userId || ! data . workspaceId || ! data . tariffPlanId ) ) {
205241 this . sendError ( res , CheckCodes . PAYMENT_COULD_NOT_BE_ACCEPTED , '[Billing / Check] There is no necessary data in the request' , body ) ;
206242
207243 return ;
@@ -235,6 +271,18 @@ export default class CloudPaymentsWebhooks {
235271 return ;
236272 }
237273
274+ if ( data . isCardLinkOperation ) {
275+ telegram
276+ . sendMessage ( `✅ [Billing / Check] Card linked for subscription workspace «${ workspace . name } »` , TelegramBotURLs . Money )
277+ . catch ( e => console . error ( 'Error while sending message to Telegram: ' + e ) ) ;
278+
279+ res . json ( {
280+ code : CheckCodes . SUCCESS ,
281+ } as CheckResponse ) ;
282+
283+ return ;
284+ }
285+
238286 /**
239287 * Create business operation about creation of subscription
240288 */
@@ -266,6 +314,7 @@ export default class CloudPaymentsWebhooks {
266314
267315 telegram . sendMessage ( `✅ [Billing / Check] All checks passed successfully «${ workspace . name } »` , TelegramBotURLs . Money )
268316 . catch ( e => console . error ( 'Error while sending message to Telegram: ' + e ) ) ;
317+
269318 HawkCatcher . send ( new Error ( '[Billing / Check] All checks passed successfully' ) , body as any ) ;
270319
271320 res . json ( {
@@ -720,9 +769,9 @@ export default class CloudPaymentsWebhooks {
720769 *
721770 * @param req - request with necessary data
722771 */
723- private async getDataFromRequest ( req : express . Request ) : Promise < PlanProlongationData > {
772+ private async getDataFromRequest ( req : express . Request ) : Promise < PaymentData > {
724773 const context = req . context ;
725- const body : CheckRequest = req . body ;
774+ const body : CheckRequest | PayRequest | FailRequest = req . body ;
726775
727776 /**
728777 * If Data is not presented in body means there is a recurring payment
@@ -752,6 +801,7 @@ export default class CloudPaymentsWebhooks {
752801 tariffPlanId : workspace . tariffPlanId . toString ( ) ,
753802 userId,
754803 shouldSaveCard : false ,
804+ isCardLinkOperation : false ,
755805 } ;
756806 }
757807
0 commit comments