Skip to content

Commit

Permalink
Integrate Stripe
Browse files Browse the repository at this point in the history
  • Loading branch information
harishmohanraj committed Jan 11, 2024
1 parent 57b39aa commit af182af
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 133 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
ADS_SERVER_URL: ${{ vars.ADS_SERVER_URL }}
STRIPE_KEY: ${{ secrets.STRIPE_KEY }}
SUBSCRIPTION_PRICE_ID: ${{ secrets.SUBSCRIPTION_PRICE_ID }}

jobs:
unit_test:
Expand Down
92 changes: 71 additions & 21 deletions src/client/AccountPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { useQuery } from "@wasp/queries";
import logout from "@wasp/auth/logout";
import stripePayment from "@wasp/actions/stripePayment";
import { useState, Dispatch, SetStateAction } from "react";
import { Link } from "@wasp/router";

// get your own link from your stripe dashboard: https://dashboard.stripe.com/settings/billing/portal
const CUSTOMER_PORTAL_LINK =
"https://billing.stripe.com/p/login/test_8wM8x17JN7DT4zC000";
const CUSTOMER_PORTAL_LINK = "";

export default function Example({ user }: { user: User }) {
const [isLoading, setIsLoading] = useState<boolean>(false);
Expand All @@ -32,34 +32,81 @@ export default function Example({ user }: { user: User }) {
{user.email}
</dd>
</div>
{/* <div className='py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6'>
<dt className='text-sm font-medium text-gray-500'>Your Plan</dt>
<div className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6">
{/* <dt className="text-sm font-medium text-gray-500">
Credits remaining
</dt>
<>
<dd className="mt-1 text-sm text-gray-900 sm:col-span-1 sm:mt-0">
{user.credits}
</dd>
<BuyMoreButton
isLoading={isLoading}
setIsLoading={setIsLoading}
/>
</> */}
{/* <dt className="text-sm font-medium text-gray-500">Your Plan</dt>
{user.hasPaid ? (
<>
<dd className='mt-1 text-sm text-gray-900 sm:col-span-1 sm:mt-0'>Premium Monthly Subscription</dd>
<CustomerPortalButton isLoading={isLoading} setIsLoading={setIsLoading} />
<dd className="mt-1 text-sm text-gray-900 sm:col-span-1 sm:mt-0">
Premium Subscription
</dd>
<CustomerPortalButton
isLoading={isLoading}
setIsLoading={setIsLoading}
/>
</>
) : (
<>
<dd className='mt-1 text-sm text-gray-900 sm:col-span-1 sm:mt-0'>
<dd className="mt-1 text-sm text-gray-900 sm:col-span-1 sm:mt-0">
Credits remaining: {user.credits}
</dd>
<BuyMoreButton isLoading={isLoading} setIsLoading={setIsLoading} />
<BuyMoreButton
isLoading={isLoading}
setIsLoading={setIsLoading}
/>
</>
)}
</div> */}
{/* <div className='py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6'>
<dt className='text-sm font-medium text-gray-500'>About</dt>
<dd className='mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0'>I'm a cool customer.</dd>
</div> */}
{/* <div className='py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6'> */}
{/* <dt className='text-sm font-medium text-gray-500'>Most Recent User RelatedObject</dt> */}
{/* <dd className='mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0'>
*/}
<dt className="text-sm font-medium text-gray-500">Your Plan</dt>
{user.hasPaid ? (
<>
<dd className="mt-1 text-sm text-gray-900 sm:col-span-1 sm:mt-0">
Premium Subscription
</dd>
<CustomerPortalButton
isLoading={isLoading}
setIsLoading={setIsLoading}
/>
</>
) : (
<>
<dd className="mt-1 text-sm text-gray-900 sm:col-span-1 sm:mt-0">
No Active Subscription
</dd>
<BuyMoreButton
isLoading={isLoading}
setIsLoading={setIsLoading}
/>
</>
)}
</div>
<div className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6">
<dt className="text-sm font-medium text-gray-500">About</dt>
<dd className="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
I'm a cool customer.
</dd>
</div>
{/* <div className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6">
<dt className="text-sm font-medium text-gray-500">
Most Recent User RelatedObject
</dt>
<dd className='mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0'>
{!!relatedObjects && relatedObjects.length > 0
? relatedObjects[relatedObjects.length - 1].content
: "You don't have any at this time."}
</dd> */}
{/* </div> */}
</dd>
</div> */}
</dl>
</div>
</div>
Expand Down Expand Up @@ -98,14 +145,17 @@ function BuyMoreButton({

return (
<div className="ml-4 flex-shrink-0 sm:col-span-1 sm:mt-0">
<button
<p className="font-medium text-sm text-indigo-600 hover:text-indigo-500">
{!isLoading ? <Link to="/pricing">Upgrade Plan</Link> : "Loading..."}
</p>
{/* <button
onClick={handleClick}
className={`font-medium text-sm text-indigo-600 hover:text-indigo-500 ${
isLoading && "animate-pulse"
}`}
>
{!isLoading ? "Buy More/Upgrade" : "Loading..."}
</button>
{!isLoading ? <Link to="/pricing">Upgrade Plan</Link> : "Loading..."}
</button> */}
</div>
);
}
Expand Down
99 changes: 62 additions & 37 deletions src/client/NavBar.tsx
Original file line number Diff line number Diff line change
@@ -1,83 +1,108 @@
import logo from "./static/captn-logo.png";
import { Disclosure } from "@headlessui/react";
import { AiOutlineBars, AiOutlineClose, AiOutlineUser } from "react-icons/ai";
import useAuth from "@wasp/auth/useAuth";

import logo from './static/captn-logo.png'
import { Disclosure } from '@headlessui/react';
import { AiOutlineBars, AiOutlineClose, AiOutlineUser } from 'react-icons/ai';
import useAuth from '@wasp/auth/useAuth';

const active = 'inline-flex items-center border-b-2 border-indigo-300 px-1 pt-1 text-sm font-medium text-gray-900';
const inactive = 'inline-flex items-center border-b-2 border-transparent px-1 pt-1 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700'
const active =
"inline-flex items-center border-b-2 border-indigo-300 px-1 pt-1 text-sm font-medium text-gray-900";
const inactive =
"inline-flex items-center border-b-2 border-transparent px-1 pt-1 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700";
const current = window.location.pathname;

export default function NavBar() {
const { data: user } = useAuth();

return (
<Disclosure as='nav' className='bg-captn-light-cream shadow sticky top-0 z-50 '>
<Disclosure
as="nav"
className="bg-captn-light-cream shadow sticky top-0 z-50 "
>
{({ open }) => (
<>
<div className='mx-auto max-w-7xl px-4 sm:px-6 lg:px-16'>
<div className='flex h-16 justify-between'>
<div className='flex'>
<div className='flex flex-shrink-0 items-center'>
<a href='/'>
<img className='h-8 w-8' src={logo} alt='My SaaS App' />
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-16">
<div className="flex h-16 justify-between">
<div className="flex">
<div className="flex flex-shrink-0 items-center">
<a href="/">
<img className="h-8 w-8" src={logo} alt="My SaaS App" />
</a>
</div>
<div className='hidden sm:ml-6 sm:flex sm:space-x-8'>
<a href='/' className={current === '/' ? active : inactive}>
<div className="hidden sm:ml-6 sm:flex sm:space-x-8">
<a href="/" className={current === "/" ? active : inactive}>
Home
</a>
{/* <a href='/pricing' className={current.includes('pricing') ? active : inactive}>
{/* <a
href="/pricing"
className={current.includes("pricing") ? active : inactive}
>
Pricing
</a> */}
{/* <a href='/gpt' className={current.includes('gpt') ? active : inactive}>
GPT
</a> */}
<a href='/chat' className={current.includes('chat') ? active : inactive}>
<a
href="/chat"
className={current.includes("chat") ? active : inactive}
>
Chat
</a>
</div>
</div>
<div className='hidden sm:ml-6 sm:flex sm:space-x-8'>
<a href={!!user ? '/account' : '/login'} className={current === '/account' ? active : inactive}>
<AiOutlineUser className='h-6 w-6 mr-2' />
<div className="hidden sm:ml-6 sm:flex sm:space-x-8">
<a
href={!!user ? "/account" : "/login"}
className={current === "/account" ? active : inactive}
>
<AiOutlineUser className="h-6 w-6 mr-2" />
Account
</a>
</div>
<div className='-mr-2 flex items-center sm:hidden'>
<div className="-mr-2 flex items-center sm:hidden">
{/* Mobile menu */}
<Disclosure.Button className='inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-300'>
<span className='sr-only'>Open menu</span>
<Disclosure.Button className="inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-300">
<span className="sr-only">Open menu</span>
{open ? (
<AiOutlineClose className='block h-6 w-6' aria-hidden='true' />
<AiOutlineClose
className="block h-6 w-6"
aria-hidden="true"
/>
) : (
<AiOutlineBars className='block h-6 w-6' aria-hidden='true' />
<AiOutlineBars
className="block h-6 w-6"
aria-hidden="true"
/>
)}
</Disclosure.Button>
</div>
</div>
</div>

<Disclosure.Panel className='sm:hidden'>
<div className='space-y-1 pt-2 pb-3'>
<Disclosure.Panel className="sm:hidden">
<div className="space-y-1 pt-2 pb-3">
<Disclosure.Button
as='a'
href='/'
className={`${current === '/' ? "bg-captn-light-blue" : ""} block border-l-4 border-transparent py-2 pl-3 pr-4 text-base font-medium text-captn-dark-blue hover:border-gray-300 hover:bg-gray-50 hover:text-captn-dark-blue`}
as="a"
href="/"
className={`${
current === "/" ? "bg-captn-light-blue" : ""
} block border-l-4 border-transparent py-2 pl-3 pr-4 text-base font-medium text-captn-dark-blue hover:border-gray-300 hover:bg-gray-50 hover:text-captn-dark-blue`}
>
Home
</Disclosure.Button>
<Disclosure.Button
as='a'
href='/chat'
className={`${current.includes('chat') ? "bg-captn-light-blue" : ""} block border-l-4 border-transparent py-2 pl-3 pr-4 text-base font-medium text-captn-dark-blue hover:border-gray-300 hover:bg-gray-50 hover:text-captn-dark-blue`}
as="a"
href="/chat"
className={`${
current.includes("chat") ? "bg-captn-light-blue" : ""
} block border-l-4 border-transparent py-2 pl-3 pr-4 text-base font-medium text-captn-dark-blue hover:border-gray-300 hover:bg-gray-50 hover:text-captn-dark-blue`}
>
Chat
</Disclosure.Button>
<Disclosure.Button
as='a'
href='/account'
className={`${current.includes('account') ? "bg-captn-light-blue" : ""} block border-l-4 border-transparent py-2 pl-3 pr-4 text-base font-medium text-captn-dark-blue hover:border-gray-300 hover:bg-gray-50 hover:text-captn-dark-blue`}
as="a"
href="/account"
className={`${
current.includes("account") ? "bg-captn-light-blue" : ""
} block border-l-4 border-transparent py-2 pl-3 pr-4 text-base font-medium text-captn-dark-blue hover:border-gray-300 hover:bg-gray-50 hover:text-captn-dark-blue`}
>
Account
</Disclosure.Button>
Expand Down
36 changes: 19 additions & 17 deletions src/client/PricingPage.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import { AiOutlineCheck } from "react-icons/ai";
import stripePayment from "@wasp/actions/stripePayment";
import { useState } from "react";
import NumericStepper from "./components/NumericStepper";

const prices = [
// {
// name: "Credits",
// id: "credits",
// href: "",
// price: "$",
// description: "Buy credits to use for your projects.",
// features: ["Use them any time", "No expiration date"],
// disabled: false,
// priceMonthly: "",
// },
{
name: "Credits",
id: "credits",
href: "",
price: "$2.95",
description: "Buy credits to use for your projects.",
features: ["10 credits", "Use them any time", "No expiration date"],
disabled: true,
},
{
name: "Monthly Subscription",
id: "monthly",
name: "Lifetime Subscription",
id: "Lifetime",
href: "#",
priceMonthly: "$9.99",
description: "Get unlimited usage for your projects.",
priceMonthly: "$1",
description: "Get access to all premium features.",
features: [
"Unlimited usage of all features",
"Priority support",
Expand Down Expand Up @@ -47,11 +49,11 @@ export default function PricingPage() {
return (
<div className="mt-10 pb-24 sm:pb-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mx-auto grid max-w-md grid-cols-1 gap-8 lg:max-w-4xl lg:grid-cols-2">
<div className="mx-auto grid max-w-md grid-cols-1 gap-8">
{prices.map((price) => (
<div
key={price.id}
className="flex flex-col justify-between rounded-3xl bg-white p-8 shadow-xl ring-1 ring-gray-900/10 sm:p-10"
className="relative flex flex-col justify-between rounded-3xl bg-white p-8 shadow-xl ring-1 ring-gray-900/10 sm:p-10"
>
<div>
<h3
Expand All @@ -64,11 +66,11 @@ export default function PricingPage() {
<span className="text-5xl font-bold tracking-tight text-gray-900">
{price.priceMonthly || price.price}
</span>
{price.priceMonthly && (
{/* {price.priceMonthly && (
<span className="text-base font-semibold leading-7 text-gray-600">
/month
</span>
)}
)} */}
</div>
<p className="mt-6 text-base leading-7 text-gray-600">
{price.description}
Expand Down
Loading

0 comments on commit af182af

Please sign in to comment.