Build a data-driven, reusable membership plan UI (based on the provided screenshots) that cycles through plans in a carousel, includes a global cart in the navbar, and implements a full checkout flow with a dedicated cart/checkout page using Stripe.
This task is focused on:
- Component design and reusability
- State management across pages
- Clean UI implementation
- Integrating a real payment provider (Stripe) using Next.js API routes
- Basic cart and checkout architecture
Stripe keys will be provided separately.
- Next.js + React + TypeScript.
- Use Tailwind CSS for styling
- State management is up to you:
- You may use local state, React Context, Zustand, Redux, etc.
- If unsure, React Context is recommended.
- Stripe must be fully integrated using Next.js API routes.
- Import Work Sans from Google Fonts and use across the whole site
- Colour codes are as follows:
- Nav Background/Brand Color:
#022a25 - Membership Bar on Carousel:
- Bronze:
#f59e0b - Silver:
#cbd5e1 - Gold:
#fde047
- Bronze:
- Nav Background/Brand Color:
- Import Icons from any icon library of your choice (react.feather or HeroIcons recommended)
- Plans page must match the 'Part A - Plan Screen.pdf' file in to the 'public' folder of the project.
- 9 different icons are required on the Plans Page. Match the sizes and colours shown in the PDF as closely as possible.
Use this array to render plan names and prices. This must be the only source of pricing data.
interface Product {
id: number;
name: string;
monthlyPrice: number;
totalMonthlyPrice: number;
totalMonthlySavings: number;
totalAnnualPrice: number;
}
export const products: Product[] = [
{
id: 1,
name: 'Bronze Membership',
monthlyPrice: 140,
totalMonthlyPrice: 1680,
totalMonthlySavings: 420,
totalAnnualPrice: 2140,
},
{
id: 2,
name: 'Silver Membership',
monthlyPrice: 240,
totalMonthlyPrice: 2880,
totalMonthlySavings: 3420,
totalAnnualPrice: 540,
},
{
id: 3,
name: 'Gold Membership',
monthlyPrice: 340,
totalMonthlyPrice: 3680,
totalMonthlySavings: 820,
totalAnnualPrice: 6820,
},
];Build a carousel that displays one plan at a time and allows switching between plans using left/right navigation arrows (like the Plan Screen screenshot).
The carousel must be:
- Data-driven
- Reusable
- Generated by mapping over the
productsarray - Not hardcoded to exactly 3 slides
Create a reusable component similar to:
MembershipPlansCarousel({ products })
Where:
productsis an array ofProduct- The component maps over
productsto generate slides - The component should work for any length of products
MembershipPlansCarousel→ handles carousel logic + navigationPlanSlide→ renders UI for a single plan
These values must come from the Product fields (not hardcoded) and must be formatted as currency.
| UI Element | Data field |
|---|---|
| Top header title | product.name |
| Main price on the right | product.monthlyPrice |
| Monthly option “Annual Cost” | product.totalMonthlyPrice |
| Annual option “Annual Cost” | product.totalAnnualPrice |
| “Your Savings” | product.totalMonthlySavings |
$240.00*→monthlyPrice$2,880.00→totalMonthlyPrice$3,420.00→totalAnnualPrice$540.00→totalMonthlySavings
Each slide should visually follow the Plan Screen screenshot.
- Header: “Membership Information”
- Row of 3 icons + labels (static content is fine)
- Feature list with green check / red cross icons (static content is fine)
This feature list does not need to come from data and can be hardcoded once.
- Header: “Payment Information”
- Large price text showing
product.monthlyPrice - Two static contact buttons:
- “Speak To Our Team”
- “Chat With Our Team”
- “Select a payment option below” section:
- Monthly Fee and Annual Fee selectable options
- “Annual Cost” row showing both totals
- “Your Savings” row
- “12-Month Contract” row (static icons are fine)
- “Purchase Membership” button
- When clicked, user is taken to
/checkout
- When clicked, user is taken to
- Implement UI state for Monthly vs Annual
- Selected option must be visually highlighted
- This selection must be persisted and available on the cart/checkout page
All money values must:
- Include dollar sign
- Include comma separators
- Always show 2 decimals
240→$240.002880→$2,880.00
A global navigation bar has been created for you in the template file. Add the functionality to this button to update with the Monthly Price for the product the user selects when they click the "Purchase Memebership" button at the bottom of the page.
Follow the Cart Behaviour instructions below
The cart represents the currently selected membership plan.
When a user selects a membership and clicks “Purchase Membership”:
- The selected plan must be added to the cart
- The cart must display:
- Selected plan name
- Selected billing type (Monthly / Annual)
- Final price
The cart does not need to support multiple items — it can be treated as a single-item cart.
Cart state must persist across pages.
You may use:
- React Context (recommended if unsure)
- Zustand / Redux / Jotai
- Or URL params (if cleanly implemented)
The same state should be used for:
- Carousel selection
- Cart display
- Checkout page
- Cart should be visible in the navbar at all times
- Cart should clearly show the current Monthly Price
- If no membership is selected:
- Cart should show an empty state (e.g. “No plan selected”)
- Clicking the cart (or checkout button in navbar) should navigate to
/checkout - The checkout page must reflect the same data shown in the cart
- Cart displays selected membership
- Cart displays correct price
- Cart persists across page navigation
- Clicking cart navigates to
/checkout - Cart state matches checkout state
This page represents a pre-checkout cart summary where the user reviews their membership before proceeding to the full checkout.
This page acts as a cart review step only (no payment processing here).
The selected membership and billing option must be available on this page via state management.
Ensure the Cart page displays the following product values from the user's selected product via state (values required will be indicated in the comments on the page which you will be required to map correctly and display via state):
- Selected plan name
- Selected billing type (Monthly / Annual)
- Base price (ex GST)
- GST amount (assume 10%)
- Final total to pay today
This data must come from the same state as the carousel selection.
- Checkbox:
“I have read and agree to the Terms & Conditions of this membership.” - The Proceed to Checkout button must be disabled until this is checked.
- Button: “Proceed to Checkout”
- Navigates to
/checkout
- Displays selected membership
- Shows correct pricing + GST
- Terms checkbox required to continue
- Button navigates to
/checkout - No Stripe logic on this page
This is the final payment step.
Create a route at /checkout that contains:
- Customer details form
- Membership summary
- Stripe payment form
This page performs the actual Stripe payment.
All required form fields have been created for you.
All fields must be controlled inputs.
Basic validation required:
- Required fields must be filled
- Email must be valid format
Ensure the Checkout page displays the following product values from the user's selected product via state (Values required will be indicated in the comments on the page which you will be required to map correctly and display via state):
- Selected plan name
- Selected billing type (Monthly / Annual)
- Base price (ex GST)
- GST amount (10%)
- Final total to pay today
Must use the same state as the cart page.
Integrate Stripe Elements to collect card details:
- Card number
- Expiry
- CVC
Do not build custom card inputs.
Use official Stripe Elements components.
This must be a real Stripe integration using:
- Stripe SDK
- Stripe Elements (frontend)
- Next.js API routes (backend)
Stripe keys will be provided separately.
You will be provided with Stripe test API keys separately.
These keys should be used for all development and testing.
Do not use real/live Stripe keys.
When testing payments with Stripe, you may use the following test card:
Card number: 4242 4242 4242 4242
Expiry: Any future date (e.g. 12/34)
CVC: Any 3 digits (e.g. 123)
ZIP / Postal code: Any valid value
This card should always result in a successful test payment.
- All payments must be made in Stripe test mode
- No real money will be charged
- The success state should be handled exactly the same way as a real payment
- Do not commit Stripe keys to the repository (use environment variables)
Create an API route such as:
POST /api/create-payment-intent
This route should:
Receive:
- Selected product
- Billing type
- Final price (including GST)
Then:
- Create a Stripe PaymentIntent
- Return
client_secretto the frontend
On checkout form submit:
- Validate customer details
- Call
/api/create-payment-intent - Use Stripe Elements to confirm card payment
- On success:
- Show success message or screen
- Clear cart and checkout state
No webhooks required for this task.
You must persist across pages:
- Selected product
- Monthly vs Annual choice
- Cart data
State management is up to you:
- React Context (recommended if unsure)
- Zustand / Redux / Jotai
- Or URL params (if cleanly implemented)
- Customer form exists and validates
- Membership summary matches cart
- Stripe Elements used for card input
- Next.js API creates PaymentIntent
- Real Stripe payment works end-to-end
- Success state shown after payment
- Cart state cleared after success
- Slides generated by mapping over
products - Title uses
product.name - Main price uses
product.monthlyPrice - Monthly “Annual Cost” uses
product.totalMonthlyPrice - Annual “Annual Cost” uses
product.totalAnnualPrice - “Your Savings” uses
product.totalMonthlySavings - Monthly/Annual toggle is interactive
- Currency formatting is correct
- Clicking “Purchase Membership” navigates to
/cart
- Selected plan persists from plans page
- Displays selected plan name and billing type
- Base price (ex GST) displayed correctly
- GST (10%) calculated correctly
- Final total to pay today is correct
- Proceed button disabled until Terms are accepted
- Clicking “Proceed to Checkout” navigates to
/checkout
- Customer details form exists and is controlled
- Required fields are validated
- Membership summary matches cart
- GST and totals calculated correctly
- Stripe Elements used for card input
- Next.js API route creates PaymentIntent
- Real Stripe payment works end-to-end
- Success state shown after payment
- Cart / checkout state cleared after success
- Global navbar exists on all pages
- Cart visible in navbar
- Cart displays correct selected price
- Cart state persists across page navigation
- Cart state, checkout state, and carousel state all stay in sync
To complete this task, you must deploy a working version of the application and provide a live link.
- Push the full project to your own GitHub repository
- Create a new project on your own Vercel account
- Deploy the project to Vercel
- Ensure the application works in the production environment
You must successfully test all three membership products in production:
- Bronze Membership
- Silver Membership
- Gold Membership
For each product:
- Select the membership on the plans page
- Proceed through cart and checkout
- Complete a Stripe payment using test keys
- Reach the success state
- Redirect the user to the /plans page after payment is completed
All three products must complete the full flow successfully.
Submit the following:
- Public GitHub repository link
- Live Vercel deployment URL
The Vercel link should allow:
- Browsing plans
- Adding to cart
- Completing checkout
- Making Stripe test payments end-to-end
No local or development-only links will be accepted.