Skip to content

Commit

Permalink
fix(payment): verify payment with orderId (#74)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsun969 authored Feb 18, 2024
1 parent cfd4b80 commit 3d61442
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 13 deletions.
3 changes: 2 additions & 1 deletion docs/redis.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ We use Redis as a cache to store things like payment IDs when membership payment
The [`node-redis`](https://www.npmjs.com/package/redis) library is used for communicating to the Redis server. We are currently using Vercel KV as our Redis instance. Note that when setting `REDIS_URI` in `.env.local`, you should be using `rediss://` rather than `redis://` as Vercel KV mandates that TLS be used. Otherwise, you will get [Error read ECONNRESET](https://vercel.com/docs/storage/vercel-kv/vercel-kv-error-codes#error-read-econnreset).

The `REDIS_URI` environment variable should follow the Redis URI syntax:

```
redis[s]://[[username][:password]@][host][:port][/db-number]
```
Expand All @@ -13,4 +14,4 @@ redis[s]://[[username][:password]@][host][:port][/db-number]

The keys that are currently used

- `payment:membership:<userId>` stores a Redis hash that stores a `paymentId` and `createdAt` timestamp.
- `payment:membership:<userId>` stores a Redis hash that stores a `orderId` and `createdAt` timestamp.
9 changes: 3 additions & 6 deletions src/app/api/payment/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export async function POST(request: Request) {
idempotencyKey: crypto.randomUUID(),
description: 'Payment made from CS Club website',
order: {
locationId: env.SQUARE_LOCATION_ID!,
locationId: env.SQUARE_LOCATION_ID,
customerId: reqBody.data.customerId,
lineItems: [lineItem],
},
Expand All @@ -72,12 +72,9 @@ export async function POST(request: Request) {

if (reqBody.data.product === 'membership') {
// Add Clerk ID and payment ID to Redis cache
const paymentId = resp.result.paymentLink?.id ?? '';
const orderId = resp.result.paymentLink?.orderId ?? '';
const createdAt = resp.result.paymentLink?.createdAt ?? '';
await redisClient.hSet(`payment:membership:${user.id}`, {
paymentId: paymentId,
createdAt: createdAt,
});
await redisClient.hSet(`payment:membership:${user.id}`, { orderId, createdAt });
}

// The URL to direct the user is accessed from `url` and `long_url`
Expand Down
14 changes: 8 additions & 6 deletions src/server/verify-membership-payment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,18 @@ export const verifyMembershipPayment = async (clerkId: string) => {
return { paid: true as const, membershipExpiresAt: member.membershipExpiresAt };
}

const paymentId = await redisClient.hGet(`payment:membership:${clerkId}`, 'paymentId');
if (!paymentId) {
const orderId = await redisClient.hGet(`payment:membership:${clerkId}`, 'orderId');
if (!orderId) {
// Membership payment for the user does not exist
return { paid: false as const };
}

const resp = await squareClient.checkoutApi.retrievePaymentLink(paymentId);
const respFields = resp.result;
if (!respFields.paymentLink || respFields.paymentLink.id !== paymentId) {
// Payment has not been made
try {
const orderRes = await squareClient.ordersApi.retrieveOrder(orderId);
if (orderRes.result.order?.state !== 'COMPLETED') {
return { paid: false as const };
}
} catch {
return { paid: false as const };
}

Expand Down

0 comments on commit 3d61442

Please sign in to comment.