What does Relay do?
Relay is a full-stack payment SaaS application that helps businesses create, send, track, and manage one-time payment requests from customers.
A business owner creates a Relay account and gets an isolated workspace. From that workspace, they can connect their own Stripe account, add customers, create payment requests, and monitor what happens after a payment link is sent.
A typical payment looks like this:
- The business owner creates a customer in Relay.
- They create a payment request with an amount, currency, and description.
- Relay creates a Stripe Checkout session through the business's connected Stripe account.
- Relay returns a payment link that can be copied or sent to the customer by email.
- The customer opens the link and completes the payment on Stripe Checkout.
- Stripe sends a webhook event back to Relay.
- Relay verifies the event and updates the payment automatically.
- The business owner can see the latest status and full payment timeline from the dashboard.
A payment can move through states such as:
PENDING → PAID → REFUNDED
PENDING → FAILED
PENDING → EXPIRED
Relay does more than simply create Stripe Checkout links. It also handles the operational side of payments.
If a payment fails, Relay can create an alert for the business owner. If the business has its own application, Relay can send signed webhook notifications to that application when a payment changes state.
Failed outbound webhook deliveries are recorded and can be replayed later from the dashboard.
Payment links can also be sent directly to customers by email using Resend, with delivery attempts and failures tracked by Relay.
-
Workspace authentication Each business has its own isolated workspace and data.
-
Customer management Create and manage customers before requesting payment.
-
Stripe Connect Connect a business Stripe account to Relay.
-
Payment requests Generate one-time Stripe Checkout payment links.
-
Email delivery Send payment links directly to customers.
-
Payment tracking Track
PENDING,PAID,FAILED,EXPIRED, andREFUNDEDpayments. -
Payment timeline View the Stripe events that caused each payment status change.
-
Operational alerts Surface failed payments and webhook deliveries.
-
Outbound webhooks Notify another application when a payment changes state.
-
Webhook replay Retry failed webhook deliveries without changing the original payment event.
-
Idempotency and event deduplication Protect against duplicate payment creation and repeated Stripe events.
The Next.js frontend provides the dashboard and public payment pages.
The NestJS backend contains the business logic for authentication, customers, payments, Stripe Connect, emails, alerts, and webhooks.
PostgreSQL stores workspace, customer, payment, event, alert, and delivery data, while Prisma provides database access.
Stripe handles account connection and card payments, while Resend is used to send payment links by email.
- NestJS
- TypeScript
- Prisma
- PostgreSQL
- Next.js
- React
- TypeScript
- Tailwind CSS
- Stripe Connect
- Stripe Checkout
- Stripe Webhooks
- Resend
- Jest
- Supertest
- Swagger / OpenAPI
Install:
- Node.js
- PostgreSQL
- Stripe CLI
You also need:
- a Stripe test account
- a Resend account if you want to test email delivery
git clone <repository-url>
cd relay
npm installCreate your environment file:
cp .env.example .envConfigure at least:
DATABASE_URL="postgresql://USER:PASSWORD@localhost:5432/payment_saas"
PORT=3000
FRONTEND_APP_URL="http://localhost:3001"
STRIPE_SECRET_KEY="sk_test_..."
STRIPE_WEBHOOK_SECRET="whsec_..."
STRIPE_CONNECT_RETURN_URL="http://localhost:3001/stripe"
STRIPE_CONNECT_REFRESH_URL="http://localhost:3001/stripe"
STRIPE_CHECKOUT_SUCCESS_URL="http://localhost:3001/payments/success?session_id={CHECKOUT_SESSION_ID}"
STRIPE_CHECKOUT_CANCEL_URL="http://localhost:3001/payments/cancel"Email delivery is optional:
RESEND_API_KEY="re_..."
EMAIL_FROM="Relay <onboarding@resend.dev>"Create a PostgreSQL database called:
payment_saas
Then run:
npx prisma generate
npx prisma migrate deploynpm run start:devThe API will run on:
http://localhost:3000
Swagger documentation is available at:
http://localhost:3000/api
Open another terminal:
npm run stripe:listenStripe CLI will print a webhook signing secret:
whsec_...
Copy it into your .env file:
STRIPE_WEBHOOK_SECRET="whsec_..."Restart the backend after changing the value.
Keep the Stripe listener running while testing payments locally. Stripe cannot send webhook events directly to localhost, so the listener forwards them to the Relay backend.
Open another terminal:
cd frontend
npm install
cp .env.example .env.local
npm run devThe frontend runs on:
http://localhost:3001
Once PostgreSQL, the backend, the frontend, and the Stripe listener are running:
- Create a Relay account.
- Connect a Stripe test account.
- Add a customer.
- Create a payment request.
- Open the generated payment link.
- Complete the payment using Stripe test mode.
- Return to Relay.
- Check the updated payment status and event timeline.
Run backend tests:
npm testRun end-to-end tests:
npm run test:e2eRun test coverage:
npm run test:covWhen the backend is running, detailed API documentation is available through Swagger:
http://localhost:3000/api
Swagger contains the individual endpoints, request bodies, responses, and authentication requirements, while this README focuses on explaining the application and how to run it locally.
