Skip to content

Commit

Permalink
feat(payment): Add payment API route
Browse files Browse the repository at this point in the history
  • Loading branch information
rayokamoto committed Jan 27, 2024
1 parent 2da9adb commit d605f85
Show file tree
Hide file tree
Showing 6 changed files with 251 additions and 7 deletions.
4 changes: 4 additions & 0 deletions .env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ CLERK_SECRET_KEY=
# Database. Do not modify in development.
DATABASE_URL=file:dev.sqlite
DATABASE_AUTH_TOKEN=

# Square
SQUARE_ACCESS_TOKEN=
SQUARE_LOCATION_ID=
5 changes: 5 additions & 0 deletions docs/square.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Working with the Square API

The `square` package from npm is used to easily interact with the Square API. Documentation for the SDK is available [here](https://developer.squareup.com/docs/sdks/nodejs).

We work with the Checkout API's payment links. [Docs](https://developer.squareup.com/reference/square/checkout-api/create-payment-link)
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"react-dom": "^18.2.0",
"react-hook-form": "^7.49.3",
"react-icons": "^4.12.0",
"square": "^34.0.1",
"zod": "^3.22.4",
"zustand": "^4.4.7"
},
Expand Down
168 changes: 161 additions & 7 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 64 additions & 0 deletions src/app/api/payment/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { type NextRequest } from 'next/server';
import type { CreatePaymentLinkRequest, OrderLineItem, CreatePaymentLinkResponse } from 'square';
import { Client, Environment } from 'square';
import { products } from '@/data/products';

const client = new Client({
accessToken: process.env.SQUARE_ACCESS_TOKEN,
environment:
process.env.NODE_ENV === 'production' ? Environment.Production : Environment.Sandbox,
});

const { checkoutApi } = client;

export async function POST(request: NextRequest) {
const params = request.nextUrl.searchParams;
const product = params.get('product');
const customerId = params.get('customerId');
const redirectUrl = params.get('redirect');

// TODO: Validate that the user is signed in

if (!product || !customerId || !redirectUrl) {
return new Response('Missing URL parameters', { status: 400 });
}

let lineItem: OrderLineItem;
if (product === 'membership') {
lineItem = products.membership;
} else {
return new Response('Product does not exist', { status: 400 });
}

const body: CreatePaymentLinkRequest = {
idempotencyKey: crypto.randomUUID(),
description: 'Payment made from CS Club website',
order: {
locationId: process.env.SQUARE_LOCATION_ID!,
customerId: customerId,
lineItems: [lineItem],
},
checkoutOptions: {
allowTipping: false,
redirectUrl: redirectUrl,
askForShippingAddress: false,
acceptedPaymentMethods: {
applePay: true,
googlePay: true,
cashAppPay: false,
afterpayClearpay: false,
},
enableCoupon: false,
enableLoyalty: false,
},
};

const resp = await checkoutApi.createPaymentLink(body);
const respFields: CreatePaymentLinkResponse = resp.result;
if (resp.statusCode != 200) {
return new Response(JSON.stringify(respFields.errors), { status: resp.statusCode });
}

// The URL to direct the user is accessed from `url` and `long_url`
return Response.json(respFields.paymentLink);
}
16 changes: 16 additions & 0 deletions src/data/products.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { OrderLineItem } from 'square';

export const products: { [key: string]: OrderLineItem } = {
membership: {
name: 'CS Club Membership 2024',
quantity: '1',
itemType: 'ITEM',
metadata: {
SKU: 'club_membership',
},
basePriceMoney: {
amount: BigInt(1000),
currency: 'AUD',
},
},
};

0 comments on commit d605f85

Please sign in to comment.