Skip to content

Commit 16708ed

Browse files
committed
fix workflow steps for reporting order failures
1 parent 8ce0e72 commit 16708ed

File tree

6 files changed

+46
-10
lines changed

6 files changed

+46
-10
lines changed

apps/order/src/app/activities/activities.service.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,8 @@ export class ActivitiesService {
1212
async createOrder(data: OrderWorkflowData) {
1313
return await this.orderService.createOrder(data);
1414
}
15+
16+
async reportPaymentFailed(orderId: number) {
17+
return this.orderService.reportPaymentFailed(orderId);
18+
}
1519
}

apps/order/src/app/order/order.service.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Injectable, Logger } from '@nestjs/common';
22
import { OrderWorkflowData } from '@projectx/core';
33
import { OrderRepositoryService } from '@projectx/db';
4+
import { OrderStatus } from '@projectx/models';
45
import { StripeService } from '@projectx/payment';
56

67
@Injectable()
@@ -35,4 +36,14 @@ export class OrderService {
3536
clientSecret: paymentIntent.client_secret,
3637
};
3738
}
39+
40+
async reportPaymentFailed(orderId: number) {
41+
this.logger.log(`reportPaymentFailed(${orderId})`);
42+
const updatedOrder = await this.orderRepositoryService.updateOrderStatus(
43+
orderId,
44+
OrderStatus.Failed
45+
);
46+
// TODO: Send email notification to user about payment failure
47+
return updatedOrder;
48+
}
3849
}

apps/order/src/workflows/order.workflow.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ import { cancelWorkflowSignal } from '../../../../libs/backend/core/src/lib/work
2323
import type { OrderStatusResponseDto } from '../../../../libs/models/src/order/order.dto';
2424
import type { ActivitiesService } from '../main';
2525

26-
const { createOrder: createOrderActivity } = proxyActivities<ActivitiesService>(
27-
{
26+
const { createOrder: createOrderActivity, reportPaymentFailed } =
27+
proxyActivities<ActivitiesService>({
2828
startToCloseTimeout: '5 seconds',
2929
retry: {
3030
initialInterval: '2s',
@@ -33,8 +33,7 @@ const { createOrder: createOrderActivity } = proxyActivities<ActivitiesService>(
3333
backoffCoefficient: 1.5,
3434
nonRetryableErrorTypes: [OrderWorkflowNonRetryableErrors.UNKNOWN_ERROR],
3535
},
36-
}
37-
);
36+
});
3837
import { processPayment } from './process-payment.workflow';
3938

4039
export enum OrderStatus {
@@ -97,8 +96,9 @@ export async function createOrder(
9796
});
9897
const processPaymentResult = await processPaymentWorkflow.result();
9998
if (processPaymentResult.status !== OrderProcessPaymentStatus.SUCCESS) {
99+
// Report payment failure before throwing the error
100+
await reportPaymentFailed(state.orderId);
100101
state.status = OrderStatus.Failed;
101-
// TODO: Send email to the user
102102
throw ApplicationFailure.nonRetryable(
103103
OrderWorkflowNonRetryableErrors.UNKNOWN_ERROR,
104104
'Payment failed'

apps/web/app/routes/checkout.$referenceId.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { LoaderFunctionArgs, MetaFunction } from '@remix-run/node';
22
import { useLoaderData } from '@remix-run/react';
3-
import { useEffect, useState } from 'react';
3+
import { useEffect, useRef, useState } from 'react';
44
import { useCart } from 'react-use-cart';
55
import invariant from 'tiny-invariant';
66

@@ -41,14 +41,17 @@ export default function Checkout() {
4141
(workflow) => workflow.referenceId === referenceId
4242
);
4343
// Trigger actions to manage the workflow
44-
const { handleRun } = useWorkflowActions<OrderWorkflow>({
44+
const { handleRun, handleClear } = useWorkflowActions<OrderWorkflow>({
4545
workflowType: WorkflowTypes.ORDER,
4646
});
47+
const workflowInitiatedRef = useRef(false);
4748
const [clientSecret, setClientSecret] = useState('');
49+
const [error, setError] = useState<Error>();
4850

4951
// Trigger the creation of the order workflow
5052
useEffect(() => {
51-
if (!currentCheckoutWorkflow && !clientSecret && items.length > 0) {
53+
if (!workflowInitiatedRef.current && !currentCheckoutWorkflow && items.length > 0) {
54+
workflowInitiatedRef.current = true;
5255
handleRun({
5356
workflow: {
5457
referenceId,
@@ -67,16 +70,25 @@ export default function Checkout() {
6770
},
6871
},
6972
});
73+
// Clear the cart after the workflow is triggered
7074
emptyCart();
7175
}
72-
}, [currentCheckoutWorkflow, clientSecret, items]);
76+
}, [currentCheckoutWorkflow, items]);
7377

7478
useEffect(() => {
7579
if (!clientSecret && currentCheckoutWorkflow?.data?.response?.clientSecret) {
7680
setClientSecret(currentCheckoutWorkflow.data.response.clientSecret);
7781
}
7882
}, [currentCheckoutWorkflow?.data?.response?.clientSecret, clientSecret]);
7983

84+
// Manage errors with the current workflow
85+
useEffect(() => {
86+
if (currentCheckoutWorkflow?.error) {
87+
setError(currentCheckoutWorkflow.error);
88+
handleClear({ workflow: currentCheckoutWorkflow });
89+
}
90+
}, [currentCheckoutWorkflow?.error]);
91+
8092
return (
8193
<PageLayout title="Checkout">
8294
<CheckoutPage clientSecret={clientSecret} />

libs/backend/db/prisma/schema.prisma

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ enum OrderStatus {
253253
Shipped
254254
Delivered
255255
Cancelled
256+
Failed
256257
}
257258

258259
enum PaymentStatus {

libs/backend/db/src/lib/order/order-repository.service.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Injectable, Logger } from '@nestjs/common';
22
import { CreateOrderDto } from '@projectx/models';
3-
import { Prisma, OrderStatus } from '@prisma/client';
3+
import { Prisma, OrderStatus, Order } from '@prisma/client';
44

55
import { PrismaService } from '../prisma.service';
66

@@ -63,4 +63,12 @@ export class OrderRepositoryService {
6363
return order;
6464
});
6565
}
66+
67+
async updateOrderStatus(orderId: number, status: OrderStatus): Promise<Order> {
68+
this.logger.verbose(`updateOrderStatus(${orderId}) - status: ${status}`);
69+
return this.prisma.order.update({
70+
where: { id: orderId },
71+
data: { status },
72+
});
73+
}
6674
}

0 commit comments

Comments
 (0)