This example demonstrates a payment retry process using Upstash Workflow.
The following example handles retrying a payment, sending emails, and suspending accounts.
import { serve } from "@upstash/workflow/nextjs";
type ChargeUserPayload = {
email: string;
};
export const { POST } = serve<ChargeUserPayload>(async (context) => {
const { email } = context.requestPayload;
for (let i = 0; i < 3; i++) {
const result = await context.run("charge customer", async () => {
try {
return await chargeCustomer(i + 1),
} catch (e) {
console.error(e);
return
}
});
if (!result) {
await context.sleep("wait for retry", 24 * 60 * 60);
} else {
const isSuspended = await context.run("check suspension", async () => {
return await checkSuspension(email);
});
if (isSuspended) {
await context.run("unsuspend user", async () => {
await unsuspendUser(email);
});
}
await context.run("send invoice email", async () => {
await sendEmail(
email,
`Payment successfull. Incoice: ${result.invoiceId}, Total cost: $${result.totalCost}`
);
});
return;
}
}
const isSuspended = await context.run("check suspension", async () => {
return await checkSuspension(email);
});
if (!isSuspended) {
await context.run("suspend user", async () => {
await suspendUser(email);
});
await context.run("send suspended email", async () => {
await sendEmail(
email,
"Your account has been suspended due to payment failure. Please update your payment method."
);
});
}
});
async function sendEmail(email: string, content: string) {
console.log("Sending email to", email, "with content:", content);
}
async function checkSuspension(email: string) {
console.log("Checking suspension status for", email);
return true;
}
async function suspendUser(email: string) {
console.log("Suspending the user", email);
}
async function unsuspendUser(email: string) {
console.log("Unsuspending the user", email);
}
async function chargeCustomer(attempt: number) {
console.log("Charging the customer");
if (attempt <= 2) {
throw new Error("Payment failed");
}
return {
invoiceId: "INV123",
totalCost: 100,
} as const;
}
const result = await context.run("charge customer", async () => {
try {
return await chargeCustomer(i + 1),
} catch (e) {
console.error(e);
return
}
});
for (let i = 0; i < 3; i++) {
if (!result) {
await context.sleep("wait for retry", 24 * 60 * 60);
} else {
return;
}
}
const isSuspended = await context.run("check suspension", async () => {
return await checkSuspension(email);
});
if (isSuspended) {
await context.run("unsuspend user", async () => {
await unsuspendUser(email);
});
}
await context.run("send invoice email", async () => {
await sendEmail(
email,
`Payment successfull. Incoice: ${result.invoiceId}, Total cost: $${result.totalCost}`
);
});
If the payment fails after 3 retries, we suspend the user and send them an email to notify them:
const isSuspended = await context.run("check suspension", async () => {
return await checkSuspension(email);
});
if (!isSuspended) {
await context.run("suspend user", async () => {
await suspendUser(email);
});
await context.run("send suspended email", async () => {
await sendEmail(
context.requestPayload.email,
"Your account has been suspended due to payment failure. Please update your payment method."
);
});
}