{
7 | res.sendStatus(200);
8 | }
9 |
--------------------------------------------------------------------------------
/shipstation-integration/src/modules/shipstation/index.ts:
--------------------------------------------------------------------------------
1 | import ShipStationProviderService from "./service"
2 | import {
3 | ModuleProvider,
4 | Modules
5 | } from "@medusajs/framework/utils"
6 |
7 | export default ModuleProvider(Modules.FULFILLMENT, {
8 | services: [ShipStationProviderService],
9 | })
--------------------------------------------------------------------------------
/stripe-saved-payment/medusa/.env.template:
--------------------------------------------------------------------------------
1 | STORE_CORS=http://localhost:8000,https://docs.medusajs.com
2 | ADMIN_CORS=http://localhost:5173,http://localhost:9000,https://docs.medusajs.com
3 | AUTH_CORS=http://localhost:5173,http://localhost:9000,https://docs.medusajs.com
4 | REDIS_URL=redis://localhost:6379
5 | JWT_SECRET=supersecret
6 | COOKIE_SECRET=supersecret
7 | DATABASE_URL=postgres://postgres@localhost/$DB_NAME # change user and password if necessary
8 | DB_NAME=medusa-stripe-saved-payment
9 | POSTGRES_URL=
10 |
11 | STRIPE_API_KEY=
--------------------------------------------------------------------------------
/stripe-saved-payment/medusa/.gitignore:
--------------------------------------------------------------------------------
1 | /dist
2 | .env
3 | .DS_Store
4 | /uploads
5 | /node_modules
6 | yarn-error.log
7 |
8 | .idea
9 |
10 | coverage
11 |
12 | !src/**
13 |
14 | ./tsconfig.tsbuildinfo
15 | medusa-db.sql
16 | build
17 | .cache
18 |
19 | .yarn/*
20 | !.yarn/patches
21 | !.yarn/plugins
22 | !.yarn/releases
23 | !.yarn/sdks
24 | !.yarn/versions
25 |
26 | .medusa
--------------------------------------------------------------------------------
/stripe-saved-payment/medusa/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
--------------------------------------------------------------------------------
/stripe-saved-payment/medusa/integration-tests/http/health.spec.ts:
--------------------------------------------------------------------------------
1 | import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
2 | jest.setTimeout(60 * 1000)
3 |
4 | medusaIntegrationTestRunner({
5 | inApp: true,
6 | env: {},
7 | testSuite: ({ api }) => {
8 | describe("Ping", () => {
9 | it("ping the server health endpoint", async () => {
10 | const response = await api.get('/health')
11 | expect(response.status).toEqual(200)
12 | })
13 | })
14 | },
15 | })
--------------------------------------------------------------------------------
/stripe-saved-payment/medusa/integration-tests/setup.js:
--------------------------------------------------------------------------------
1 | const { MetadataStorage } = require("@mikro-orm/core")
2 |
3 | MetadataStorage.clear()
--------------------------------------------------------------------------------
/stripe-saved-payment/medusa/src/admin/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true
22 | },
23 | "include": ["."]
24 | }
--------------------------------------------------------------------------------
/stripe-saved-payment/medusa/src/admin/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
--------------------------------------------------------------------------------
/stripe-saved-payment/medusa/src/api/middlewares.ts:
--------------------------------------------------------------------------------
1 | import { authenticate, defineMiddlewares } from "@medusajs/framework/http";
2 |
3 | export default defineMiddlewares({
4 | routes: [
5 | {
6 | matcher: "/store/payment-methods/:provider_id/:account_holder_id",
7 | method: "GET",
8 | middlewares: [
9 | authenticate("customer", ["bearer", "session"])
10 | ]
11 | }
12 | ]
13 | })
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ["next/core-web-vitals"]
3 | };
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "arrowParens": "always",
3 | "semi": false,
4 | "endOfLine": "auto",
5 | "singleQuote": false,
6 | "tabWidth": 2,
7 | "trailingComma": "es5"
8 | }
9 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | compressionLevel: mixed
2 |
3 | enableGlobalCache: false
4 |
5 | nodeLinker: node-modules
6 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
6 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/next-sitemap.js:
--------------------------------------------------------------------------------
1 | const excludedPaths = ["/checkout", "/account/*"]
2 |
3 | module.exports = {
4 | siteUrl: process.env.NEXT_PUBLIC_VERCEL_URL,
5 | generateRobotsTxt: true,
6 | exclude: excludedPaths + ["/[sitemap]"],
7 | robotsTxtOptions: {
8 | policies: [
9 | {
10 | userAgent: "*",
11 | allow: "/",
12 | },
13 | {
14 | userAgent: "*",
15 | disallow: excludedPaths,
16 | },
17 | ],
18 | },
19 | }
20 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/medusajs/examples/7a3102ddf3e17f9f833f1843d2f0ba77d910c468/stripe-saved-payment/storefront/public/favicon.ico
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/app/[countryCode]/(main)/account/@dashboard/loading.tsx:
--------------------------------------------------------------------------------
1 | import Spinner from "@modules/common/icons/spinner"
2 |
3 | export default function Loading() {
4 | return (
5 |
6 |
7 |
8 | )
9 | }
10 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/app/[countryCode]/(main)/account/@login/page.tsx:
--------------------------------------------------------------------------------
1 | import { Metadata } from "next"
2 |
3 | import LoginTemplate from "@modules/account/templates/login-template"
4 |
5 | export const metadata: Metadata = {
6 | title: "Sign in",
7 | description: "Sign in to your Medusa Store account.",
8 | }
9 |
10 | export default function Login() {
11 | return
12 | }
13 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/app/[countryCode]/(main)/account/layout.tsx:
--------------------------------------------------------------------------------
1 | import { retrieveCustomer } from "@lib/data/customer"
2 | import { Toaster } from "@medusajs/ui"
3 | import AccountLayout from "@modules/account/templates/account-layout"
4 |
5 | export default async function AccountPageLayout({
6 | dashboard,
7 | login,
8 | }: {
9 | dashboard?: React.ReactNode
10 | login?: React.ReactNode
11 | }) {
12 | const customer = await retrieveCustomer().catch(() => null)
13 |
14 | return (
15 |
16 | {customer ? dashboard : login}
17 |
18 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/app/[countryCode]/(main)/account/loading.tsx:
--------------------------------------------------------------------------------
1 | import Spinner from "@modules/common/icons/spinner"
2 |
3 | export default function Loading() {
4 | return (
5 |
6 |
7 |
8 | )
9 | }
10 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/app/[countryCode]/(main)/cart/loading.tsx:
--------------------------------------------------------------------------------
1 | import SkeletonCartPage from "@modules/skeletons/templates/skeleton-cart-page"
2 |
3 | export default function Loading() {
4 | return
5 | }
6 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/app/[countryCode]/(main)/cart/page.tsx:
--------------------------------------------------------------------------------
1 | import { retrieveCart } from "@lib/data/cart"
2 | import { retrieveCustomer } from "@lib/data/customer"
3 | import CartTemplate from "@modules/cart/templates"
4 | import { Metadata } from "next"
5 | import { notFound } from "next/navigation"
6 |
7 | export const metadata: Metadata = {
8 | title: "Cart",
9 | description: "View your cart",
10 | }
11 |
12 | export default async function Cart() {
13 | const cart = await retrieveCart().catch((error) => {
14 | console.error(error)
15 | return notFound()
16 | })
17 |
18 | const customer = await retrieveCustomer()
19 |
20 | return
21 | }
22 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/app/[countryCode]/(main)/order/[id]/confirmed/loading.tsx:
--------------------------------------------------------------------------------
1 | import SkeletonOrderConfirmed from "@modules/skeletons/templates/skeleton-order-confirmed"
2 |
3 | export default function Loading() {
4 | return
5 | }
6 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import { getBaseURL } from "@lib/util/env"
2 | import { Metadata } from "next"
3 | import "styles/globals.css"
4 |
5 | export const metadata: Metadata = {
6 | metadataBase: new URL(getBaseURL()),
7 | }
8 |
9 | export default function RootLayout(props: { children: React.ReactNode }) {
10 | return (
11 |
12 |
13 | {props.children}
14 |
15 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/app/opengraph-image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/medusajs/examples/7a3102ddf3e17f9f833f1843d2f0ba77d910c468/stripe-saved-payment/storefront/src/app/opengraph-image.jpg
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/app/twitter-image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/medusajs/examples/7a3102ddf3e17f9f833f1843d2f0ba77d910c468/stripe-saved-payment/storefront/src/app/twitter-image.jpg
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/lib/config.ts:
--------------------------------------------------------------------------------
1 | import Medusa from "@medusajs/js-sdk"
2 |
3 | // Defaults to standard port for Medusa server
4 | let MEDUSA_BACKEND_URL = "http://localhost:9000"
5 |
6 | if (process.env.MEDUSA_BACKEND_URL) {
7 | MEDUSA_BACKEND_URL = process.env.MEDUSA_BACKEND_URL
8 | }
9 |
10 | export const sdk = new Medusa({
11 | baseUrl: MEDUSA_BACKEND_URL,
12 | debug: process.env.NODE_ENV === "development",
13 | publishableKey: process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY,
14 | })
15 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/lib/data/onboarding.ts:
--------------------------------------------------------------------------------
1 | "use server"
2 | import { cookies as nextCookies } from "next/headers"
3 | import { redirect } from "next/navigation"
4 |
5 | export async function resetOnboardingState(orderId: string) {
6 | const cookies = await nextCookies()
7 | cookies.set("_medusa_onboarding", "false", { maxAge: -1 })
8 | redirect(`http://localhost:7001/a/orders/${orderId}`)
9 | }
10 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/lib/util/compare-addresses.ts:
--------------------------------------------------------------------------------
1 | import { isEqual, pick } from "lodash"
2 |
3 | export default function compareAddresses(address1: any, address2: any) {
4 | return isEqual(
5 | pick(address1, [
6 | "first_name",
7 | "last_name",
8 | "address_1",
9 | "company",
10 | "postal_code",
11 | "city",
12 | "country_code",
13 | "province",
14 | "phone",
15 | ]),
16 | pick(address2, [
17 | "first_name",
18 | "last_name",
19 | "address_1",
20 | "company",
21 | "postal_code",
22 | "city",
23 | "country_code",
24 | "province",
25 | "phone",
26 | ])
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/lib/util/env.ts:
--------------------------------------------------------------------------------
1 | export const getBaseURL = () => {
2 | return process.env.NEXT_PUBLIC_BASE_URL || "https://localhost:8000"
3 | }
4 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/lib/util/get-precentage-diff.ts:
--------------------------------------------------------------------------------
1 | export const getPercentageDiff = (original: number, calculated: number) => {
2 | const diff = original - calculated
3 | const decrease = (diff / original) * 100
4 |
5 | return decrease.toFixed()
6 | }
7 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/lib/util/isEmpty.ts:
--------------------------------------------------------------------------------
1 | export const isObject = (input: any) => input instanceof Object
2 | export const isArray = (input: any) => Array.isArray(input)
3 | export const isEmpty = (input: any) => {
4 | return (
5 | input === null ||
6 | input === undefined ||
7 | (isObject(input) && Object.keys(input).length === 0) ||
8 | (isArray(input) && (input as any[]).length === 0) ||
9 | (typeof input === "string" && input.trim().length === 0)
10 | )
11 | }
12 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/lib/util/product.ts:
--------------------------------------------------------------------------------
1 | import { HttpTypes } from "@medusajs/types";
2 |
3 | export const isSimpleProduct = (product: HttpTypes.StoreProduct): boolean => {
4 | return product.options?.length === 1 && product.options[0].values?.length === 1;
5 | }
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/lib/util/repeat.ts:
--------------------------------------------------------------------------------
1 | const repeat = (times: number) => {
2 | return Array.from(Array(times).keys())
3 | }
4 |
5 | export default repeat
6 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/modules/checkout/components/error-message/index.tsx:
--------------------------------------------------------------------------------
1 | const ErrorMessage = ({ error, 'data-testid': dataTestid }: { error?: string | null, 'data-testid'?: string }) => {
2 | if (!error) {
3 | return null
4 | }
5 |
6 | return (
7 |
8 | {error}
9 |
10 | )
11 | }
12 |
13 | export default ErrorMessage
14 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/modules/checkout/components/payment-test/index.tsx:
--------------------------------------------------------------------------------
1 | import { Badge } from "@medusajs/ui"
2 |
3 | const PaymentTest = ({ className }: { className?: string }) => {
4 | return (
5 |
6 | Attention: For testing purposes
7 | only.
8 |
9 | )
10 | }
11 |
12 | export default PaymentTest
13 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/modules/common/components/divider/index.tsx:
--------------------------------------------------------------------------------
1 | import { clx } from "@medusajs/ui"
2 |
3 | const Divider = ({ className }: { className?: string }) => (
4 |
7 | )
8 |
9 | export default Divider
10 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/modules/home/components/featured-products/index.tsx:
--------------------------------------------------------------------------------
1 | import { HttpTypes } from "@medusajs/types"
2 | import ProductRail from "@modules/home/components/featured-products/product-rail"
3 |
4 | export default async function FeaturedProducts({
5 | collections,
6 | region,
7 | }: {
8 | collections: HttpTypes.StoreCollection[]
9 | region: HttpTypes.StoreRegion
10 | }) {
11 | return collections.map((collection) => (
12 |
13 |
14 |
15 | ))
16 | }
17 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/modules/layout/components/cart-button/index.tsx:
--------------------------------------------------------------------------------
1 | import { retrieveCart } from "@lib/data/cart"
2 | import CartDropdown from "../cart-dropdown"
3 |
4 | export default async function CartButton() {
5 | const cart = await retrieveCart().catch(() => null)
6 |
7 | return
8 | }
9 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/modules/layout/components/medusa-cta/index.tsx:
--------------------------------------------------------------------------------
1 | import { Text } from "@medusajs/ui"
2 |
3 | import Medusa from "../../../common/icons/medusa"
4 | import NextJs from "../../../common/icons/nextjs"
5 |
6 | const MedusaCTA = () => {
7 | return (
8 |
9 | Powered by
10 |
11 |
12 |
13 | &
14 |
15 |
16 |
17 |
18 | )
19 | }
20 |
21 | export default MedusaCTA
22 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/modules/layout/templates/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | import Footer from "@modules/layout/templates/footer"
4 | import Nav from "@modules/layout/templates/nav"
5 |
6 | const Layout: React.FC<{
7 | children: React.ReactNode
8 | }> = ({ children }) => {
9 | return (
10 |
11 |
12 | {children}
13 |
14 |
15 | )
16 | }
17 |
18 | export default Layout
19 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/modules/skeletons/components/skeleton-button/index.tsx:
--------------------------------------------------------------------------------
1 | const SkeletonButton = () => {
2 | return
3 | }
4 |
5 | export default SkeletonButton
6 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/modules/skeletons/components/skeleton-card-details/index.tsx:
--------------------------------------------------------------------------------
1 | const SkeletonCardDetails = () => {
2 | return (
3 |
7 | )
8 | }
9 |
10 | export default SkeletonCardDetails
11 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/modules/skeletons/components/skeleton-code-form/index.tsx:
--------------------------------------------------------------------------------
1 | const SkeletonCodeForm = () => {
2 | return (
3 |
10 | )
11 | }
12 |
13 | export default SkeletonCodeForm
14 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/modules/skeletons/components/skeleton-order-confirmed-header/index.tsx:
--------------------------------------------------------------------------------
1 | const SkeletonOrderConfirmedHeader = () => {
2 | return (
3 |
11 | )
12 | }
13 |
14 | export default SkeletonOrderConfirmedHeader
15 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/modules/skeletons/components/skeleton-order-summary/index.tsx:
--------------------------------------------------------------------------------
1 | import SkeletonButton from "@modules/skeletons/components/skeleton-button"
2 | import SkeletonCartTotals from "@modules/skeletons/components/skeleton-cart-totals"
3 |
4 | const SkeletonOrderSummary = () => {
5 | return (
6 |
12 | )
13 | }
14 |
15 | export default SkeletonOrderSummary
16 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/modules/skeletons/components/skeleton-product-preview/index.tsx:
--------------------------------------------------------------------------------
1 | import { Container } from "@medusajs/ui"
2 |
3 | const SkeletonProductPreview = () => {
4 | return (
5 |
12 | )
13 | }
14 |
15 | export default SkeletonProductPreview
16 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/types/global.ts:
--------------------------------------------------------------------------------
1 | import { StorePrice } from "@medusajs/types"
2 |
3 | export type FeaturedProduct = {
4 | id: string
5 | title: string
6 | handle: string
7 | thumbnail?: string
8 | }
9 |
10 | export type VariantPrice = {
11 | calculated_price_number: number
12 | calculated_price: string
13 | original_price_number: number
14 | original_price: string
15 | currency_code: string
16 | price_type: string
17 | percentage_diff: string
18 | }
19 |
20 | export type StoreFreeShippingPrice = StorePrice & {
21 | target_reached: boolean
22 | target_remaining: number
23 | remaining_percentage: number
24 | }
25 |
--------------------------------------------------------------------------------
/stripe-saved-payment/storefront/src/types/icon.ts:
--------------------------------------------------------------------------------
1 | export type IconProps = {
2 | color?: string
3 | size?: string | number
4 | } & React.SVGAttributes
5 |
--------------------------------------------------------------------------------
/subscription/.env.template:
--------------------------------------------------------------------------------
1 | MEDUSA_ADMIN_ONBOARDING_TYPE=default
2 | STORE_CORS=http://localhost:8000,https://docs.medusajs.com
3 | ADMIN_CORS=http://localhost:7000,http://localhost:7001,https://docs.medusajs.com
4 | AUTH_CORS=http://localhost:7000,http://localhost:7001,https://docs.medusajs.com
5 | REDIS_URL=redis://localhost:6379
6 | JWT_SECRET=supersecret
7 | COOKIE_SECRET=supersecret
8 | DATABASE_URL=postgres://postgres@localhost/$DB_NAME # change user and password if necessary
9 | DB_NAME=
10 | STRIPE_API_KEY=
--------------------------------------------------------------------------------
/subscription/.gitignore:
--------------------------------------------------------------------------------
1 | /dist
2 | .env
3 | .DS_Store
4 | /uploads
5 | /node_modules
6 | yarn-error.log
7 |
8 | .idea
9 |
10 | coverage
11 |
12 | !src/**
13 |
14 | ./tsconfig.tsbuildinfo
15 | package-lock.json
16 | medusa-db.sql
17 | build
18 | .cache
19 |
20 | .yarn/*
21 | !.yarn/patches
22 | !.yarn/plugins
23 | !.yarn/releases
24 | !.yarn/sdks
25 | !.yarn/versions
26 |
--------------------------------------------------------------------------------
/subscription/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
--------------------------------------------------------------------------------
/subscription/src/admin/lib/sdk.ts:
--------------------------------------------------------------------------------
1 | import Medusa from "@medusajs/js-sdk"
2 |
3 | export const sdk = new Medusa({
4 | baseUrl: import.meta.env.VITE_BACKEND_URL || "/",
5 | debug: import.meta.env.DEV,
6 | auth: {
7 | type: "session",
8 | },
9 | })
--------------------------------------------------------------------------------
/subscription/src/admin/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true
22 | },
23 | "include": ["."]
24 | }
--------------------------------------------------------------------------------
/subscription/src/admin/types/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | OrderDTO,
3 | CustomerDTO
4 | } from "@medusajs/framework/types"
5 |
6 | export enum SubscriptionStatus {
7 | ACTIVE = "active",
8 | CANCELED = "canceled",
9 | EXPIRED = "expired",
10 | FAILED = "failed"
11 | }
12 |
13 | export enum SubscriptionInterval {
14 | MONTHLY = "monthly",
15 | YEARLY = "yearly"
16 | }
17 |
18 | export type SubscriptionData = {
19 | id: string
20 | status: SubscriptionStatus
21 | interval: SubscriptionInterval
22 | subscription_date: string
23 | last_order_date: string
24 | next_order_date: string | null
25 | expiration_date: string
26 | metadata: Record | null
27 | orders?: OrderDTO[]
28 | customer?: CustomerDTO
29 | }
--------------------------------------------------------------------------------
/subscription/src/admin/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
--------------------------------------------------------------------------------
/subscription/src/api/admin/subscriptions/[id]/route.ts:
--------------------------------------------------------------------------------
1 | import {
2 | AuthenticatedMedusaRequest,
3 | MedusaResponse
4 | } from "@medusajs/framework";
5 | import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
6 |
7 | export const GET = async (
8 | req: AuthenticatedMedusaRequest,
9 | res: MedusaResponse
10 | ) => {
11 | const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
12 |
13 | const { data: [subscription] } = await query.graph({
14 | entity: "subscription",
15 | fields: [
16 | "*",
17 | "orders.*",
18 | "customer.*",
19 | ],
20 | filters: {
21 | id: [req.params.id]
22 | }
23 | })
24 |
25 | res.json({
26 | subscription
27 | })
28 | }
--------------------------------------------------------------------------------
/subscription/src/api/admin/subscriptions/route.ts:
--------------------------------------------------------------------------------
1 | import {
2 | AuthenticatedMedusaRequest,
3 | MedusaResponse
4 | } from "@medusajs/framework";
5 | import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
6 |
7 | export const GET = async (
8 | req: AuthenticatedMedusaRequest,
9 | res: MedusaResponse
10 | ) => {
11 | const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
12 |
13 | const {
14 | data: subscriptions,
15 | metadata: { count, take, skip },
16 | } = await query.graph({
17 | entity: "subscription",
18 | ...req.queryConfig,
19 | })
20 |
21 | res.json({
22 | subscriptions,
23 | count,
24 | limit: take,
25 | offset: skip
26 | })
27 | }
--------------------------------------------------------------------------------
/subscription/src/api/store/customers/me/subscriptions/route.ts:
--------------------------------------------------------------------------------
1 | import {
2 | AuthenticatedMedusaRequest,
3 | MedusaResponse
4 | } from "@medusajs/framework";
5 | import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
6 |
7 | export const GET = async (
8 | req: AuthenticatedMedusaRequest,
9 | res: MedusaResponse
10 | ) => {
11 | const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
12 |
13 | const { data: [customer] } = await query.graph({
14 | entity: "customer",
15 | fields: [
16 | "subscriptions.*"
17 | ],
18 | filters: {
19 | id: [req.auth_context.actor_id]
20 | }
21 | })
22 |
23 | res.json({
24 | subscriptions: customer.subscriptions
25 | })
26 | }
--------------------------------------------------------------------------------
/subscription/src/links/subscription-cart.ts:
--------------------------------------------------------------------------------
1 | import { defineLink } from "@medusajs/framework/utils"
2 | import SubscriptionModule from "../modules/subscription"
3 | import CartModule from "@medusajs/medusa/cart"
4 |
5 | export default defineLink(
6 | SubscriptionModule.linkable.subscription,
7 | CartModule.linkable.cart
8 | )
--------------------------------------------------------------------------------
/subscription/src/links/subscription-customer.ts:
--------------------------------------------------------------------------------
1 | import { defineLink } from "@medusajs/framework/utils"
2 | import SubscriptionModule from "../modules/subscription"
3 | import CustomerModule from "@medusajs/medusa/customer"
4 |
5 | export default defineLink(
6 | {
7 | linkable: SubscriptionModule.linkable.subscription.id,
8 | isList: true
9 | },
10 | CustomerModule.linkable.customer
11 | )
--------------------------------------------------------------------------------
/subscription/src/links/subscription-order.ts:
--------------------------------------------------------------------------------
1 | import { defineLink } from "@medusajs/framework/utils"
2 | import SubscriptionModule from "../modules/subscription"
3 | import OrderModule from "@medusajs/medusa/order"
4 |
5 | export default defineLink(
6 | SubscriptionModule.linkable.subscription,
7 | {
8 | linkable: OrderModule.linkable.order.id,
9 | isList: true
10 | }
11 | )
--------------------------------------------------------------------------------
/subscription/src/modules/subscription/index.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@medusajs/framework/utils"
2 | import SubscriptionModuleService from "./service"
3 |
4 | export const SUBSCRIPTION_MODULE = "subscriptionModuleService"
5 |
6 | export default Module(SUBSCRIPTION_MODULE, {
7 | service: SubscriptionModuleService
8 | })
--------------------------------------------------------------------------------
/subscription/src/modules/subscription/models/subscription.ts:
--------------------------------------------------------------------------------
1 | import { model } from "@medusajs/framework/utils"
2 | import { SubscriptionInterval, SubscriptionStatus } from "../types"
3 |
4 | const Subscription = model.define("subscription", {
5 | id: model.id().primaryKey(),
6 | status: model.enum(SubscriptionStatus)
7 | .default(SubscriptionStatus.ACTIVE),
8 | interval: model.enum(SubscriptionInterval),
9 | period: model.number(),
10 | subscription_date: model.dateTime(),
11 | last_order_date: model.dateTime(),
12 | next_order_date: model.dateTime().index().nullable(),
13 | expiration_date: model.dateTime().index(),
14 | metadata: model.json().nullable()
15 | })
16 |
17 | export default Subscription
--------------------------------------------------------------------------------
/subscription/src/modules/subscription/types/index.ts:
--------------------------------------------------------------------------------
1 | import { InferTypeOf } from "@medusajs/framework/types"
2 | import Subscription from "../models/subscription"
3 |
4 | export enum SubscriptionStatus {
5 | ACTIVE = "active",
6 | CANCELED = "canceled",
7 | EXPIRED = "expired",
8 | FAILED = "failed"
9 | }
10 |
11 | export enum SubscriptionInterval {
12 | MONTHLY = "monthly",
13 | YEARLY = "yearly"
14 | }
15 |
16 | export type CreateSubscriptionData = {
17 | interval: SubscriptionInterval
18 | period: number
19 | status?: SubscriptionStatus
20 | subscription_date?: Date
21 | metadata?: Record
22 | }
23 |
24 | export type SubscriptionData = InferTypeOf
--------------------------------------------------------------------------------
/wishlist-plugin/.gitignore:
--------------------------------------------------------------------------------
1 | /dist
2 | .env
3 | .DS_Store
4 | /uploads
5 | /node_modules
6 | yarn-error.log
7 |
8 | .idea
9 |
10 | coverage
11 |
12 | !src/**
13 |
14 | ./tsconfig.tsbuildinfo
15 | medusa-db.sql
16 | build
17 | .cache
18 |
19 | .yarn/*
20 | !.yarn/patches
21 | !.yarn/plugins
22 | !.yarn/releases
23 | !.yarn/sdks
24 | !.yarn/versions
25 |
26 | .medusa
--------------------------------------------------------------------------------
/wishlist-plugin/src/admin/lib/sdk.ts:
--------------------------------------------------------------------------------
1 | import Medusa from "@medusajs/js-sdk"
2 |
3 | export const sdk = new Medusa({
4 | baseUrl: import.meta.env.VITE_BACKEND_URL || "/",
5 | debug: import.meta.env.DEV,
6 | auth: {
7 | type: "session",
8 | },
9 | })
--------------------------------------------------------------------------------
/wishlist-plugin/src/admin/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true
22 | },
23 | "include": ["."]
24 | }
--------------------------------------------------------------------------------
/wishlist-plugin/src/admin/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
--------------------------------------------------------------------------------
/wishlist-plugin/src/api/middlewares.ts:
--------------------------------------------------------------------------------
1 | import {
2 | defineMiddlewares,
3 | validateAndTransformBody,
4 | } from "@medusajs/framework/http"
5 | import { PostStoreCreateWishlistItem } from "./store/customers/me/wishlists/items/validators"
6 |
7 | export default defineMiddlewares({
8 | routes: [
9 | {
10 | matcher: "/store/customers/me/wishlists/items",
11 | method: "POST",
12 | middlewares: [
13 | validateAndTransformBody(PostStoreCreateWishlistItem),
14 | ],
15 | },
16 | ],
17 | })
--------------------------------------------------------------------------------
/wishlist-plugin/src/api/store/customers/me/wishlists/items/[id]/route.ts:
--------------------------------------------------------------------------------
1 | import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/framework";
2 | import { deleteWishlistItemWorkflow } from "../../../../../../../workflows/delete-wishlist-item";
3 |
4 | export async function DELETE(
5 | req: AuthenticatedMedusaRequest,
6 | res: MedusaResponse
7 | ) {
8 | const { result } = await deleteWishlistItemWorkflow(req.scope)
9 | .run({
10 | input: {
11 | wishlist_item_id: req.params.id,
12 | customer_id: req.auth_context.actor_id
13 | }
14 | })
15 |
16 | res.json({
17 | wishlist: result.wishlist
18 | })
19 | }
--------------------------------------------------------------------------------
/wishlist-plugin/src/api/store/customers/me/wishlists/items/validators.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod"
2 |
3 | export const PostStoreCreateWishlistItem = z.object({
4 | variant_id: z.string(),
5 | })
--------------------------------------------------------------------------------
/wishlist-plugin/src/links/wishlist-customer.ts:
--------------------------------------------------------------------------------
1 | import { defineLink } from "@medusajs/framework/utils"
2 | import WishlistModule from "../modules/wishlist"
3 | import CustomerModule from "@medusajs/medusa/customer"
4 |
5 | export default defineLink(
6 | {
7 | linkable: WishlistModule.linkable.wishlist.id,
8 | field: "customer_id"
9 | },
10 | CustomerModule.linkable.customer.id,
11 | {
12 | readOnly: true
13 | }
14 | )
--------------------------------------------------------------------------------
/wishlist-plugin/src/links/wishlist-product.ts:
--------------------------------------------------------------------------------
1 | import { defineLink } from "@medusajs/framework/utils"
2 | import WishlistModule from "../modules/wishlist"
3 | import ProductModule from "@medusajs/medusa/product"
4 |
5 | export default defineLink(
6 | {
7 | linkable: WishlistModule.linkable.wishlistItem.id,
8 | field: "product_variant_id"
9 | },
10 | ProductModule.linkable.productVariant,
11 | {
12 | readOnly: true
13 | }
14 | )
--------------------------------------------------------------------------------
/wishlist-plugin/src/links/wishlist-sales-channel.ts:
--------------------------------------------------------------------------------
1 | import { defineLink } from "@medusajs/framework/utils"
2 | import WishlistModule from "../modules/wishlist"
3 | import SalesChannelModule from "@medusajs/medusa/sales-channel"
4 |
5 | export default defineLink(
6 | {
7 | linkable: WishlistModule.linkable.wishlist.id,
8 | field: "sales_channel_id"
9 | },
10 | SalesChannelModule.linkable.salesChannel,
11 | {
12 | readOnly: true
13 | }
14 | )
--------------------------------------------------------------------------------
/wishlist-plugin/src/modules/wishlist/index.ts:
--------------------------------------------------------------------------------
1 | import WishlistModuleService from "./service"
2 | import { Module } from "@medusajs/framework/utils"
3 |
4 | export const WISHLIST_MODULE = "wishlist"
5 |
6 | export default Module(WISHLIST_MODULE, {
7 | service: WishlistModuleService,
8 | })
--------------------------------------------------------------------------------
/wishlist-plugin/src/modules/wishlist/models/wishlist-item.ts:
--------------------------------------------------------------------------------
1 | import { model } from "@medusajs/framework/utils"
2 | import { Wishlist } from "./wishlist"
3 |
4 | export const WishlistItem = model.define("wishlist_item", {
5 | id: model.id().primaryKey(),
6 | product_variant_id: model.text(),
7 | wishlist: model.belongsTo(() => Wishlist, {
8 | mappedBy: "items"
9 | })
10 | })
11 | .indexes([
12 | {
13 | on: ["product_variant_id", "wishlist_id"],
14 | unique: true
15 | }
16 | ])
--------------------------------------------------------------------------------
/wishlist-plugin/src/modules/wishlist/models/wishlist.ts:
--------------------------------------------------------------------------------
1 | import { model } from "@medusajs/framework/utils"
2 | import { WishlistItem } from "./wishlist-item"
3 |
4 | export const Wishlist = model.define("wishlist", {
5 | id: model.id().primaryKey(),
6 | customer_id: model.text(),
7 | sales_channel_id: model.text(),
8 | items: model.hasMany(() => WishlistItem),
9 | })
10 | .indexes([
11 | {
12 | on: ["customer_id", "sales_channel_id"],
13 | unique: true
14 | }
15 | ])
--------------------------------------------------------------------------------
/wishlist-plugin/src/workflows/steps/validate-wishlist-exists.ts:
--------------------------------------------------------------------------------
1 | import { MedusaError } from "@medusajs/framework/utils"
2 | import { createStep } from "@medusajs/framework/workflows-sdk"
3 | import { InferTypeOf } from "@medusajs/framework/types"
4 | import { Wishlist } from "../../modules/wishlist/models/wishlist"
5 |
6 | type Input = {
7 | wishlists?: InferTypeOf[]
8 | }
9 |
10 | export const validateWishlistExistsStep = createStep(
11 | "validate-wishlist-exists",
12 | async (input: Input) => {
13 | if (!input.wishlists?.length) {
14 | throw new MedusaError(
15 | MedusaError.Types.NOT_FOUND,
16 | "No wishlist found for this customer"
17 | )
18 | }
19 | }
20 | )
--------------------------------------------------------------------------------