Skip to content

Commit c0e7cf4

Browse files
Invalidate a successfull donation (#597)
* Reduce the cache ttl for public donations and total money collected. The idea of the cache is to help in extreme scenarios when many requests are being fired. One request every 2 seconds should be easy to handle by the backend. * The expense original filenames are encoded in base64. This allows us to upload files with cyrilic names. But it adds a bit of complexity in the backend. * Add support for making a donation invalid. Sometimes we can have a buggy stripe donation and we would like to sort of remove it. This is allowed only if one has special credentials, of course. * Refund payment should also be covered by the account-edit-financials-requests role. * Rename the post /invalidate-stripe-payment/:id to a patch /:id/invalidate to match the REST guidelines better.
1 parent d21fea3 commit c0e7cf4

File tree

5 files changed

+68
-3
lines changed

5 files changed

+68
-3
lines changed

apps/api/src/donations/donations.controller.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,4 +297,28 @@ describe('DonationsController', () => {
297297
reason: 'requested_by_customer',
298298
})
299299
})
300+
301+
it('should invalidate a donation and update the vault if needed', async () => {
302+
const existingDonation = { ...mockDonation, status: DonationStatus.succeeded }
303+
jest.spyOn(prismaMock, '$transaction').mockImplementation((callback) => callback(prismaMock))
304+
305+
prismaMock.donation.findFirstOrThrow.mockResolvedValueOnce(existingDonation)
306+
307+
await controller.invalidate('123')
308+
309+
expect(prismaMock.donation.update).toHaveBeenCalledWith({
310+
where: { id: '123' },
311+
data: {
312+
status: DonationStatus.invalid,
313+
},
314+
})
315+
expect(prismaMock.vault.update).toHaveBeenCalledWith({
316+
where: { id: existingDonation.targetVaultId },
317+
data: {
318+
amount: {
319+
decrement: existingDonation.amount,
320+
},
321+
},
322+
})
323+
})
300324
})

apps/api/src/donations/donations.controller.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
import { ApiQuery, ApiTags } from '@nestjs/swagger'
1717
import { DonationStatus } from '@prisma/client'
1818
import { AuthenticatedUser, Public, RoleMatchingMode, Roles } from 'nest-keycloak-connect'
19-
import { RealmViewSupporters, ViewSupporters } from '@podkrepi-bg/podkrepi-types'
19+
import { RealmViewSupporters, ViewSupporters, EditFinancialsRequests } from '@podkrepi-bg/podkrepi-types'
2020

2121
import { isAdmin, KeycloakTokenParsed } from '../auth/keycloak'
2222
import { DonationsService } from './donations.service'
@@ -221,7 +221,7 @@ export class DonationsController {
221221

222222
@Post('/refund-stripe-payment/:id')
223223
@Roles({
224-
roles: [RealmViewSupporters.role, ViewSupporters.role],
224+
roles: [EditFinancialsRequests.role],
225225
mode: RoleMatchingMode.ANY,
226226
})
227227
refundStripePaymet(@Param('id') paymentIntentId: string) {
@@ -240,6 +240,16 @@ export class DonationsController {
240240
return this.donationsService.createUpdateBankPayment(bankPaymentDto)
241241
}
242242

243+
@Patch('/:id/invalidate')
244+
@Roles({
245+
roles: [EditFinancialsRequests.role],
246+
mode: RoleMatchingMode.ANY,
247+
})
248+
invalidate(@Param('id') id: string) {
249+
Logger.debug(`Invalidating donation with id ${id}`)
250+
return this.donationsService.invalidate(id)
251+
}
252+
243253
@Patch(':id')
244254
@Roles({
245255
roles: [RealmViewSupporters.role, ViewSupporters.role],
@@ -251,12 +261,14 @@ export class DonationsController {
251261
@Body()
252262
updatePaymentDto: UpdatePaymentDto,
253263
) {
264+
Logger.debug(`Updating donation with id ${id}`)
265+
254266
return this.donationsService.update(id, updatePaymentDto)
255267
}
256268

257269
@Post('delete')
258270
@Roles({
259-
roles: [RealmViewSupporters.role, ViewSupporters.role],
271+
roles: [EditFinancialsRequests.role],
260272
mode: RoleMatchingMode.ANY,
261273
})
262274
delete(

apps/api/src/donations/donations.service.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,31 @@ export class DonationsService {
712712
}
713713
}
714714

715+
async invalidate(id: string) {
716+
try {
717+
await this.prisma.$transaction(async (tx) => {
718+
const donation = await this.getDonationById(id)
719+
720+
if (donation.status === DonationStatus.succeeded) {
721+
await this.vaultService.decrementVaultAmount(donation.targetVaultId, donation.amount, tx)
722+
}
723+
724+
await this.prisma.donation.update({
725+
where: { id },
726+
data: {
727+
status: DonationStatus.invalid,
728+
},
729+
})
730+
})
731+
} catch (err) {
732+
Logger.warn(err.message || err)
733+
const msg = `Invalidation failed. No Donation found with given ID.`
734+
735+
Logger.warn(msg)
736+
throw new NotFoundException(msg)
737+
}
738+
}
739+
715740
async getDonationsByUser(keycloakId: string, email?: string) {
716741
const donations = await this.prisma.donation.findMany({
717742
where: {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export class EditFinancialsRequests {
2+
static readonly role = 'realm:account-edit-financials-requests'
3+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './view-supporters'
22
export * from './view-contact-requests'
3+
export * from './edit-financials'

0 commit comments

Comments
 (0)