@@ -23,7 +23,9 @@ import {
2323 DatabaseFetchError ,
2424 DatabaseInsertError ,
2525 InternalServerError ,
26+ NotFoundError ,
2627 UnauthenticatedError ,
28+ UnauthorizedError ,
2729 ValidationError ,
2830} from "common/errors/index.js" ;
2931import { Modules } from "common/modules.js" ;
@@ -39,6 +41,7 @@ import stripe, { Stripe } from "stripe";
3941import rawbody from "fastify-raw-body" ;
4042import { AvailableSQSFunctions , SQSPayload } from "common/types/sqsMessage.js" ;
4143import { SendMessageCommand , SQSClient } from "@aws-sdk/client-sqs" ;
44+ import { z } from "zod" ;
4245
4346const stripeRoutes : FastifyPluginAsync = async ( fastify , _options ) => {
4447 await fastify . register ( rawbody , {
@@ -177,6 +180,113 @@ const stripeRoutes: FastifyPluginAsync = async (fastify, _options) => {
177180 reply . status ( 201 ) . send ( { id : linkId , link : url } ) ;
178181 } ,
179182 ) ;
183+ fastify . withTypeProvider < FastifyZodOpenApiTypeProvider > ( ) . delete (
184+ "/paymentLinks/:linkId" ,
185+ {
186+ schema : withRoles (
187+ [ AppRoles . STRIPE_LINK_CREATOR ] ,
188+ withTags ( [ "Stripe" ] , {
189+ summary : "Deactivate a Stripe payment link." ,
190+ params : z . object ( {
191+ linkId : z . string ( ) . min ( 1 ) . openapi ( {
192+ description : "Payment Link ID" ,
193+ example : "plink_abc123" ,
194+ } ) ,
195+ } ) ,
196+ response : { 201 : z . null ( ) } ,
197+ } ) ,
198+ ) ,
199+ onRequest : fastify . authorizeFromSchema ,
200+ } ,
201+ async ( request , reply ) => {
202+ if ( ! request . username ) {
203+ throw new UnauthenticatedError ( { message : "No username found" } ) ;
204+ }
205+ const { linkId } = request . params ;
206+ const response = await fastify . dynamoClient . send (
207+ new QueryCommand ( {
208+ TableName : genericConfig . StripeLinksDynamoTableName ,
209+ IndexName : "LinkIdIndex" ,
210+ KeyConditionExpression : "linkId = :linkId" ,
211+ ExpressionAttributeValues : {
212+ ":linkId" : { S : linkId } ,
213+ } ,
214+ } ) ,
215+ ) ;
216+ if ( ! response ) {
217+ throw new DatabaseFetchError ( {
218+ message : "Could not check for payment link in table." ,
219+ } ) ;
220+ }
221+ if ( ! response . Items || response . Items ?. length !== 1 ) {
222+ throw new NotFoundError ( { endpointName : request . url } ) ;
223+ }
224+ const unmarshalledEntry = unmarshall ( response . Items [ 0 ] ) as {
225+ userId : string ;
226+ invoiceId : string ;
227+ amount ?: number ;
228+ priceId ?: string ;
229+ productId ?: string ;
230+ } ;
231+ if (
232+ unmarshalledEntry . userId !== request . username &&
233+ ! request . userRoles ?. has ( AppRoles . BYPASS_OBJECT_LEVEL_AUTH )
234+ ) {
235+ throw new UnauthorizedError ( {
236+ message : "Not authorized to deactivate this payment link." ,
237+ } ) ;
238+ }
239+ const logStatement = buildAuditLogTransactPut ( {
240+ entry : {
241+ module : Modules . STRIPE ,
242+ actor : request . username ,
243+ target : `Link ${ linkId } | Invoice ${ unmarshalledEntry . invoiceId } ` ,
244+ message : "Deactivated Stripe payment link" ,
245+ } ,
246+ } ) ;
247+ const dynamoCommand = new TransactWriteItemsCommand ( {
248+ TransactItems : [
249+ logStatement ,
250+ {
251+ Update : {
252+ TableName : genericConfig . StripeLinksDynamoTableName ,
253+ Key : {
254+ userId : { S : unmarshalledEntry . userId } ,
255+ linkId : { S : linkId } ,
256+ } ,
257+ UpdateExpression : "SET active = :new_val" ,
258+ ConditionExpression : "active = :old_val" ,
259+ ExpressionAttributeValues : {
260+ ":new_val" : { BOOL : false } ,
261+ ":old_val" : { BOOL : true } ,
262+ } ,
263+ } ,
264+ } ,
265+ ] ,
266+ } ) ;
267+ const secretApiConfig =
268+ ( await getSecretValue (
269+ fastify . secretsManagerClient ,
270+ genericConfig . ConfigSecretName ,
271+ ) ) || { } ;
272+ if ( unmarshalledEntry . productId ) {
273+ request . log . debug (
274+ `Deactivating Stripe product ${ unmarshalledEntry . productId } ` ,
275+ ) ;
276+ await deactivateStripeProduct ( {
277+ stripeApiKey : secretApiConfig . stripe_secret_key as string ,
278+ productId : unmarshalledEntry . productId ,
279+ } ) ;
280+ }
281+ request . log . debug ( `Deactivating Stripe link ${ linkId } ` ) ;
282+ await deactivateStripeLink ( {
283+ stripeApiKey : secretApiConfig . stripe_secret_key as string ,
284+ linkId,
285+ } ) ;
286+ await fastify . dynamoClient . send ( dynamoCommand ) ;
287+ return reply . status ( 201 ) . send ( ) ;
288+ } ,
289+ ) ;
180290 fastify . post (
181291 "/webhook" ,
182292 {
0 commit comments