Skip to content

Commit

Permalink
chore(application-system): Payments updates to lifecycle on callback,…
Browse files Browse the repository at this point in the history
… UX fixes. (#15883)

* fixes to swagger and updates to lifecycle on callback, screen updates

* pr fixes

* fix tests

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
obmagnusson and kodiakhq[bot] authored Sep 24, 2024
1 parent c5d69fd commit 9c26661
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,16 @@ beforeAll(async () => {
describe('Application system payments callback API', () => {
// Sets the payment status to paid.
it(`POST /application-payment/32eee126-6b7f-4fca-b9a0-a3618b3e42bf/6b11dc9f-a694-440e-b3dd-7163b5f34815 should update payment fulfilled`, async () => {
await server
const response = await server
.post(
'/application-payment/32eee126-6b7f-4fca-b9a0-a3618b3e42bf/6b11dc9f-a694-440e-b3dd-7163b5f34815',
)
.send({
callback: {
receptionID: '1234567890',
chargeItemSubject: 'Very nice subject',
status: 'paid',
},
receptionID: '123e4567-e89b-12d3-a456-426614174000', // Updated to real UUID
chargeItemSubject: 'Very nice subject',
status: 'paid',
})
.expect(201)
expect(response.status).toBe(201)
})

// Fails to set the payment status to paid.
Expand All @@ -37,11 +35,9 @@ describe('Application system payments callback API', () => {
'/application-payment/32eee126-6b7f-4fca-b9a0-a3618b3e42bf/missing-id',
)
.send({
callback: {
receptionID: '1234567890',
chargeItemSubject: 'nice subject.. not',
status: 'paid',
},
receptionID: '123e4567-e89b-12d3-a456-426614174000', // Updated to real UUID
chargeItemSubject: 'nice subject.. not',
status: 'paid',
})
.expect(400)
})
Expand Down
1 change: 1 addition & 0 deletions apps/application-system/api/src/openApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ export const openApi = new DocumentBuilder()
.setVersion('1.0')
.addTag('application')
.addTag('payment')
.addTag('payment-callback')
.addBearerAuth()
.build()
26 changes: 22 additions & 4 deletions libs/api/domains/payment/src/lib/api-domains-payment.types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { IsEnum, IsString } from 'class-validator'
import { ApiProperty } from '@nestjs/swagger'

export interface ChargeResult {
success: boolean
error: Error | null
Expand All @@ -14,8 +17,23 @@ export interface CallbackResult {
data?: Callback
}

export interface Callback {
receptionID: string
chargeItemSubject: string
status: 'paid' | 'cancelled' | 'recreated' | 'recreatedAndPaid'
export enum PaidStatus {
paid = 'paid',
cancelled = 'cancelled',
recreated = 'recreated',
recreatedAndPaid = 'recreatedAndPaid',
}

export class Callback {
@IsString()
@ApiProperty()
readonly receptionID!: string

@IsString()
@ApiProperty()
readonly chargeItemSubject!: string

@IsEnum(PaidStatus)
@ApiProperty({ enum: PaidStatus })
readonly status!: PaidStatus
}
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ export class ApplicationService {
| 'applicantActors'
| 'draftTotalSteps'
| 'draftFinishedSteps'
| 'pruneAt'
>
>,
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import { Body, Controller, Param, Post, ParseUUIDPipe } from '@nestjs/common'
import type { Callback } from '@island.is/api/domains/payment'
import { Callback } from '@island.is/api/domains/payment'
import { PaymentService } from './payment.service'
import { ApplicationService } from '@island.is/application/api/core'
import { ApiTags } from '@nestjs/swagger'
import addMonths from 'date-fns/addMonths'

@ApiTags('payment-callback')
@Controller()
export class PaymentCallbackController {
constructor(private readonly paymentService: PaymentService) {}
constructor(
private readonly paymentService: PaymentService,
private readonly applicationService: ApplicationService,
) {}

@Post('application-payment/:applicationId/:id')
async paymentApproved(
@Param('applicationId', new ParseUUIDPipe()) applicationId: string,
@Body() callback: Callback,
@Param('applicationId', new ParseUUIDPipe()) applicationId: string,
@Param('id', new ParseUUIDPipe()) id: string,
): Promise<void> {
if (callback.status !== 'paid') {
Expand All @@ -21,5 +28,17 @@ export class PaymentCallbackController {
callback.receptionID,
applicationId,
)

const application = await this.applicationService.findOneById(applicationId)
if (application) {
const oneMonthFromNow = addMonths(new Date(), 1)
//Applications payment states are default to be pruned in 24 hours.
//If the application is paid, we want to hold on to it for longer in case we get locked in an error state.

await this.applicationService.update(applicationId, {
...application,
pruneAt: oneMonthFromNow,
})
}
}
}
6 changes: 6 additions & 0 deletions libs/application/core/src/lib/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,12 @@ export const coreErrorMessages = defineMessages({
defaultMessage: 'Sending umsóknar mistókst',
description: 'Message indicating submission after payment failed',
},
paymentSubmitFailedDescription: {
id: 'application.system:core.payment.submitTitle',
defaultMessage:
'Villa hefur komið upp við áframhaldandi vinnslu. Vinsamlegast reynið aftur síðar. Ef villa endurtekur sig vinsamlegast hafið samband við island@island.is.',
description: 'Message indicating submission after payment failed',
},
applicationSubmitFailed: {
id: 'application.system:core.application.SubmitFailed',
defaultMessage: 'Sending umsóknar mistókst',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ import {
DefaultEvents,
FieldBaseProps,
} from '@island.is/application/types'
import { Box, Button, Text } from '@island.is/island-ui/core'
import {
AlertMessage,
Box,
Button,
LoadingDots,
Text,
} from '@island.is/island-ui/core'
import { useSubmitApplication, usePaymentStatus, useMsg } from './hooks'
import { getRedirectStatus, getRedirectUrl, isComingFromRedirect } from './util'
import { Company } from './assets'
import { useSearchParams } from 'react-router-dom'

export interface PaymentPendingProps {
Expand Down Expand Up @@ -84,19 +89,45 @@ export const PaymentPending: FC<
if (submitError) {
return (
<Box>
<Text variant="h3">{msg(coreErrorMessages.paymentSubmitFailed)}</Text>
<Button onClick={() => refetch?.()}>
{msg(coreErrorMessages.paymentSubmitRetryButtonCaption)}
</Button>
<Box
marginTop={4}
display="flex"
width="full"
alignItems="center"
justifyContent="center"
style={{
height: 400,
}}
>
<AlertMessage
type="error"
title={msg(coreErrorMessages.paymentSubmitFailed)}
message={msg(coreErrorMessages.paymentSubmitFailedDescription)}
/>
</Box>
<Box>
<Button onClick={() => refetch?.()}>
{msg(coreErrorMessages.paymentSubmitRetryButtonCaption)}
</Button>
</Box>
</Box>
)
}

return (
<Box height="full">
<Text variant="h3">{msg(coreMessages.paymentPollingIndicator)}</Text>
<Box marginTop={4}>
<Company altText={msg(coreMessages.paymentPollingIndicator)} />
<Box
marginTop={4}
display="flex"
width="full"
alignItems="center"
justifyContent="center"
style={{
height: 400,
}}
>
<LoadingDots large />
</Box>
</Box>
)
Expand Down

0 comments on commit 9c26661

Please sign in to comment.