diff --git a/apps/web/lib/actions/partners/create-dots-transfer.ts b/apps/web/lib/actions/partners/create-dots-transfer.ts index e414dba98a..9acf4b5760 100644 --- a/apps/web/lib/actions/partners/create-dots-transfer.ts +++ b/apps/web/lib/actions/partners/create-dots-transfer.ts @@ -1,5 +1,6 @@ "use server"; +import { createIdempotencyKey } from "@/lib/dots/idempotency"; import { prisma } from "@/lib/prisma"; import { z } from "zod"; import { createOrgTransfer } from "../../dots/create-org-transfer"; @@ -44,10 +45,12 @@ export const createDotsTransferAction = authActionClient amount: payout.amount, dotsAppId: workspace.dotsAppId, dotsUserId: programEnrollment.dotsUserId, + idempotencyKey: await createIdempotencyKey(`transfer_${payoutId}`), }), createOrgTransfer({ amount: payout.fee, dotsAppId: workspace.dotsAppId, + idempotencyKey: await createIdempotencyKey(`org_transfer_${payoutId}`), }), ]); diff --git a/apps/web/lib/dots/create-org-transfer.ts b/apps/web/lib/dots/create-org-transfer.ts index 9e2f463ff9..0354915a91 100644 --- a/apps/web/lib/dots/create-org-transfer.ts +++ b/apps/web/lib/dots/create-org-transfer.ts @@ -3,9 +3,11 @@ import { dotsFetch } from "./fetch"; export const createOrgTransfer = async ({ amount, dotsAppId, + idempotencyKey, }: { amount: number; dotsAppId: string; + idempotencyKey: string; }) => { console.log(`Creating an org transfer of ${amount} cents`); @@ -14,6 +16,7 @@ export const createOrgTransfer = async ({ body: { amount, api_app_id: dotsAppId, + idempotency_key: idempotencyKey, }, }); }; diff --git a/apps/web/lib/dots/create-transfer.ts b/apps/web/lib/dots/create-transfer.ts index 0711b12365..30f661c204 100644 --- a/apps/web/lib/dots/create-transfer.ts +++ b/apps/web/lib/dots/create-transfer.ts @@ -4,10 +4,12 @@ export const createTransfer = async ({ amount, dotsAppId, dotsUserId, + idempotencyKey, }: { amount: number; dotsAppId: string; dotsUserId: string; + idempotencyKey: string; }) => { console.log(`Creating a transfer of ${amount} cents`); @@ -17,6 +19,7 @@ export const createTransfer = async ({ body: { user_id: dotsUserId, amount: -amount, // negative means transfer from Business to Partner + idempotency_key: idempotencyKey, }, }); }; diff --git a/apps/web/lib/dots/idempotency.ts b/apps/web/lib/dots/idempotency.ts new file mode 100644 index 0000000000..f86777477b --- /dev/null +++ b/apps/web/lib/dots/idempotency.ts @@ -0,0 +1,13 @@ +import { redis } from "@/lib/upstash"; + +export const createIdempotencyKey = async (id: string) => { + const idempotencyKey = crypto.randomUUID(); + + const oldValue = await redis.set(id, idempotencyKey, { + ex: 24 * 60 * 60, // In Dots API, idempotency keys have a 24-hour timeout. + nx: true, + get: true, + }); + + return oldValue || idempotencyKey; +};