Added prettier and prettied the code.
Created and added the razorpay payment module
This commit is contained in:
6
.prettierrc
Normal file
6
.prettierrc
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"jsxSingleQuote": true,
|
||||
"printWidth": 120
|
||||
}
|
||||
@ -5,18 +5,18 @@ The `medusa-test-utils` package provides utility functions to create integration
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { medusaIntegrationTestRunner } from "medusa-test-utils";
|
||||
import { medusaIntegrationTestRunner } from 'medusa-test-utils';
|
||||
|
||||
medusaIntegrationTestRunner({
|
||||
testSuite: ({ api, getContainer }) => {
|
||||
describe("Custom endpoints", () => {
|
||||
describe("GET /store/custom", () => {
|
||||
it("returns correct message", async () => {
|
||||
describe('Custom endpoints', () => {
|
||||
describe('GET /store/custom', () => {
|
||||
it('returns correct message', async () => {
|
||||
const response = await api.get(`/store/custom`);
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.data).toHaveProperty("message");
|
||||
expect(response.data.message).toEqual("Hello, World!");
|
||||
expect(response.data).toHaveProperty('message');
|
||||
expect(response.data.message).toEqual('Hello, World!');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { medusaIntegrationTestRunner } from "medusa-test-utils";
|
||||
import { medusaIntegrationTestRunner } from 'medusa-test-utils';
|
||||
jest.setTimeout(60 * 1000);
|
||||
|
||||
medusaIntegrationTestRunner({
|
||||
inApp: true,
|
||||
env: {},
|
||||
testSuite: ({ api }) => {
|
||||
describe("Ping", () => {
|
||||
it("ping the server health endpoint", async () => {
|
||||
const response = await api.get("/health");
|
||||
describe('Ping', () => {
|
||||
it('ping the server health endpoint', async () => {
|
||||
const response = await api.get('/health');
|
||||
expect(response.status).toEqual(200);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,26 +1,26 @@
|
||||
const { loadEnv } = require("@medusajs/utils");
|
||||
loadEnv("test", process.cwd());
|
||||
const { loadEnv } = require('@medusajs/utils');
|
||||
loadEnv('test', process.cwd());
|
||||
|
||||
module.exports = {
|
||||
transform: {
|
||||
"^.+\\.[jt]s$": [
|
||||
"@swc/jest",
|
||||
'^.+\\.[jt]s$': [
|
||||
'@swc/jest',
|
||||
{
|
||||
jsc: {
|
||||
parser: { syntax: "typescript", decorators: true },
|
||||
parser: { syntax: 'typescript', decorators: true },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
testEnvironment: "node",
|
||||
moduleFileExtensions: ["js", "ts", "json"],
|
||||
modulePathIgnorePatterns: ["dist/"],
|
||||
testEnvironment: 'node',
|
||||
moduleFileExtensions: ['js', 'ts', 'json'],
|
||||
modulePathIgnorePatterns: ['dist/'],
|
||||
};
|
||||
|
||||
if (process.env.TEST_TYPE === "integration:http") {
|
||||
module.exports.testMatch = ["**/integration-tests/http/*.spec.[jt]s"];
|
||||
} else if (process.env.TEST_TYPE === "integration:modules") {
|
||||
module.exports.testMatch = ["**/src/modules/*/__tests__/**/*.[jt]s"];
|
||||
} else if (process.env.TEST_TYPE === "unit") {
|
||||
module.exports.testMatch = ["**/src/**/__tests__/**/*.unit.spec.[jt]s"];
|
||||
if (process.env.TEST_TYPE === 'integration:http') {
|
||||
module.exports.testMatch = ['**/integration-tests/http/*.spec.[jt]s'];
|
||||
} else if (process.env.TEST_TYPE === 'integration:modules') {
|
||||
module.exports.testMatch = ['**/src/modules/*/__tests__/**/*.[jt]s'];
|
||||
} else if (process.env.TEST_TYPE === 'unit') {
|
||||
module.exports.testMatch = ['**/src/**/__tests__/**/*.unit.spec.[jt]s'];
|
||||
}
|
||||
|
||||
@ -1,53 +1,52 @@
|
||||
import { loadEnv, defineConfig } from "@medusajs/framework/utils";
|
||||
import { Modules } from "@medusajs/framework/utils";
|
||||
import { loadEnv, defineConfig } from '@medusajs/framework/utils';
|
||||
import { Modules } from '@medusajs/framework/utils';
|
||||
|
||||
loadEnv(process.env.NODE_ENV || "development", process.cwd());
|
||||
loadEnv(process.env.NODE_ENV || 'development', process.cwd());
|
||||
|
||||
module.exports = defineConfig({
|
||||
admin: {
|
||||
disable: process.env.DISABLE_MEDUSA_ADMIN === "true",
|
||||
disable: process.env.DISABLE_MEDUSA_ADMIN === 'true',
|
||||
},
|
||||
projectConfig: {
|
||||
databaseUrl: process.env.DATABASE_URL,
|
||||
database_ssl: true, // or remove if not needed
|
||||
workerMode: process.env.MEDUSA_WORKER_MODE,
|
||||
database_ssl: true, // or remove if not needed
|
||||
workerMode: process.env.MEDUSA_WORKER_MODE || 'server',
|
||||
http: {
|
||||
storeCors: process.env.STORE_CORS,
|
||||
adminCors: process.env.ADMIN_CORS,
|
||||
authCors: process.env.AUTH_CORS,
|
||||
jwtSecret: process.env.JWT_SECRET || "supersecret",
|
||||
cookieSecret: process.env.COOKIE_SECRET || "supersecret",
|
||||
storeCors: process.env.STORE_CORS!,
|
||||
adminCors: process.env.ADMIN_CORS!,
|
||||
authCors: process.env.AUTH_CORS!,
|
||||
jwtSecret: process.env.JWT_SECRET || 'supersecret',
|
||||
cookieSecret: process.env.COOKIE_SECRET || 'supersecret',
|
||||
},
|
||||
},
|
||||
modules: {
|
||||
[Modules.CACHE]: {
|
||||
resolve: "@medusajs/medusa/cache-redis",
|
||||
modules: [
|
||||
{
|
||||
resolve: '@medusajs/medusa/cache-redis',
|
||||
options: {
|
||||
redisUrl: process.env.CACHE_REDIS_URL,
|
||||
},
|
||||
},
|
||||
[Modules.EVENT_BUS]: {
|
||||
resolve: "@medusajs/medusa/event-bus-redis",
|
||||
{
|
||||
resolve: '@medusajs/medusa/event-bus-redis',
|
||||
options: {
|
||||
redisUrl: process.env.CACHE_REDIS_URL,
|
||||
},
|
||||
},
|
||||
[Modules.WORKFLOW_ENGINE]: {
|
||||
resolve: "@medusajs/workflow-engine-redis",
|
||||
{
|
||||
resolve: '@medusajs/workflow-engine-redis',
|
||||
options: {
|
||||
redis: {
|
||||
url: process.env.CACHE_REDIS_URL,
|
||||
},
|
||||
redisurl: process.env.CACHE_REDIS_URL,
|
||||
},
|
||||
},
|
||||
[Modules.FILE]: {
|
||||
resolve: "@medusajs/file",
|
||||
{
|
||||
resolve: '@medusajs/file',
|
||||
options: {
|
||||
providers: [
|
||||
{
|
||||
resolve: "@medusajs/medusa/file-s3",
|
||||
id: "s3",
|
||||
resolve: '@medusajs/medusa/file-s3',
|
||||
id: 's3',
|
||||
options: {
|
||||
file_url: process.env.S3_FILE_URL,
|
||||
access_key_id: process.env.S3_ACCESS_KEY_ID,
|
||||
@ -66,5 +65,20 @@ module.exports = defineConfig({
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
resolve: '@medusajs/medusa/payment',
|
||||
options: {
|
||||
providers: [
|
||||
{
|
||||
resolve: './src/modules/razorpay',
|
||||
id: 'razorpay',
|
||||
options: {
|
||||
key_id: process.env.RAZORPAY_KEY_ID || '',
|
||||
key_secret: process.env.RAZORPAY_KEY_SECRET || '',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@ -35,7 +35,8 @@
|
||||
"@mikro-orm/migrations": "^6.4.3",
|
||||
"@mikro-orm/postgresql": "^6.4.3",
|
||||
"awilix": "^8.0.1",
|
||||
"pg": "^8.13.0"
|
||||
"pg": "^8.13.0",
|
||||
"razorpay": "^2.9.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@medusajs/test-utils": "^2.4.0",
|
||||
@ -47,6 +48,7 @@
|
||||
"@types/react": "^18.3.2",
|
||||
"@types/react-dom": "^18.2.25",
|
||||
"jest": "^29.7.0",
|
||||
"prettier": "^3.5.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
||||
@ -9,7 +9,7 @@ A widget is a React component that can be injected into an existing page in the
|
||||
For example, create the file `src/admin/widgets/product-widget.tsx` with the following content:
|
||||
|
||||
```tsx title="src/admin/widgets/product-widget.tsx"
|
||||
import { defineWidgetConfig } from "@medusajs/admin-sdk";
|
||||
import { defineWidgetConfig } from '@medusajs/admin-sdk';
|
||||
|
||||
// The widget
|
||||
const ProductWidget = () => {
|
||||
@ -22,7 +22,7 @@ const ProductWidget = () => {
|
||||
|
||||
// The widget's configurations
|
||||
export const config = defineWidgetConfig({
|
||||
zone: "product.details.after",
|
||||
zone: 'product.details.after',
|
||||
});
|
||||
|
||||
export default ProductWidget;
|
||||
|
||||
@ -7,11 +7,11 @@ An API Route is created in a TypeScript or JavaScript file under the `/src/api`
|
||||
For example, to create a `GET` API Route at `/store/hello-world`, create the file `src/api/store/hello-world/route.ts` with the following content:
|
||||
|
||||
```ts
|
||||
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework";
|
||||
import type { MedusaRequest, MedusaResponse } from '@medusajs/framework';
|
||||
|
||||
export async function GET(req: MedusaRequest, res: MedusaResponse) {
|
||||
res.json({
|
||||
message: "Hello world!",
|
||||
message: 'Hello world!',
|
||||
});
|
||||
}
|
||||
```
|
||||
@ -33,7 +33,7 @@ You can define a handler for each of these methods by exporting a function with
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework";
|
||||
import type { MedusaRequest, MedusaResponse } from '@medusajs/framework';
|
||||
|
||||
export async function GET(req: MedusaRequest, res: MedusaResponse) {
|
||||
// Handle GET requests
|
||||
@ -55,7 +55,7 @@ To create an API route that accepts a path parameter, create a directory within
|
||||
For example, if you want to define a route that takes a `productId` parameter, you can do so by creating a file called `/api/products/[productId]/route.ts`:
|
||||
|
||||
```ts
|
||||
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework";
|
||||
import type { MedusaRequest, MedusaResponse } from '@medusajs/framework';
|
||||
|
||||
export async function GET(req: MedusaRequest, res: MedusaResponse) {
|
||||
const { productId } = req.params;
|
||||
@ -75,14 +75,12 @@ For example, if you want to define a route that takes both a `productId` and a `
|
||||
The Medusa container is available on `req.scope`. Use it to access modules' main services and other registered resources:
|
||||
|
||||
```ts
|
||||
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework";
|
||||
import { IProductModuleService } from "@medusajs/framework/types";
|
||||
import { Modules } from "@medusajs/framework/utils";
|
||||
import type { MedusaRequest, MedusaResponse } from '@medusajs/framework';
|
||||
import { IProductModuleService } from '@medusajs/framework/types';
|
||||
import { Modules } from '@medusajs/framework/utils';
|
||||
|
||||
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||
const productModuleService: IProductModuleService = req.scope.resolve(
|
||||
Modules.PRODUCT,
|
||||
);
|
||||
const productModuleService: IProductModuleService = req.scope.resolve(Modules.PRODUCT);
|
||||
|
||||
const [, count] = await productModuleService.listAndCount();
|
||||
|
||||
@ -99,26 +97,18 @@ You can apply middleware to your routes by creating a file called `/api/middlewa
|
||||
For example, if you want to apply a custom middleware function to the `/store/custom` route, you can do so by adding the following to your `/api/middlewares.ts` file:
|
||||
|
||||
```ts
|
||||
import { defineMiddlewares } from "@medusajs/medusa";
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
MedusaNextFunction,
|
||||
} from "@medusajs/framework";
|
||||
import { defineMiddlewares } from '@medusajs/medusa';
|
||||
import type { MedusaRequest, MedusaResponse, MedusaNextFunction } from '@medusajs/framework';
|
||||
|
||||
async function logger(
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse,
|
||||
next: MedusaNextFunction,
|
||||
) {
|
||||
console.log("Request received");
|
||||
async function logger(req: MedusaRequest, res: MedusaResponse, next: MedusaNextFunction) {
|
||||
console.log('Request received');
|
||||
next();
|
||||
}
|
||||
|
||||
export default defineMiddlewares({
|
||||
routes: [
|
||||
{
|
||||
matcher: "/store/custom",
|
||||
matcher: '/store/custom',
|
||||
middlewares: [logger],
|
||||
},
|
||||
],
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework";
|
||||
import { MedusaRequest, MedusaResponse } from '@medusajs/framework';
|
||||
|
||||
export async function GET(
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse,
|
||||
): Promise<void> {
|
||||
export async function GET(req: MedusaRequest, res: MedusaResponse): Promise<void> {
|
||||
res.sendStatus(200);
|
||||
}
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework";
|
||||
import { MedusaRequest, MedusaResponse } from '@medusajs/framework';
|
||||
|
||||
export async function GET(
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse,
|
||||
): Promise<void> {
|
||||
export async function GET(req: MedusaRequest, res: MedusaResponse): Promise<void> {
|
||||
res.sendStatus(200);
|
||||
}
|
||||
|
||||
@ -7,16 +7,11 @@ A scheduled job is created in a TypeScript or JavaScript file under the `src/job
|
||||
For example, create the file `src/jobs/hello-world.ts` with the following content:
|
||||
|
||||
```ts
|
||||
import {
|
||||
IProductModuleService,
|
||||
MedusaContainer,
|
||||
} from "@medusajs/framework/types";
|
||||
import { Modules } from "@medusajs/framework/utils";
|
||||
import { IProductModuleService, MedusaContainer } from '@medusajs/framework/types';
|
||||
import { Modules } from '@medusajs/framework/utils';
|
||||
|
||||
export default async function myCustomJob(container: MedusaContainer) {
|
||||
const productService: IProductModuleService = container.resolve(
|
||||
Modules.PRODUCT,
|
||||
);
|
||||
const productService: IProductModuleService = container.resolve(Modules.PRODUCT);
|
||||
|
||||
const products = await productService.listAndCountProducts();
|
||||
|
||||
@ -24,8 +19,8 @@ export default async function myCustomJob(container: MedusaContainer) {
|
||||
}
|
||||
|
||||
export const config = {
|
||||
name: "daily-product-report",
|
||||
schedule: "0 0 * * *", // Every day at midnight
|
||||
name: 'daily-product-report',
|
||||
schedule: '0 0 * * *', // Every day at midnight
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
@ -5,14 +5,11 @@ A module link forms an association between two data models of different modules,
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import HelloModule from "../modules/hello";
|
||||
import ProductModule from "@medusajs/medusa/product";
|
||||
import { defineLink } from "@medusajs/framework/utils";
|
||||
import HelloModule from '../modules/hello';
|
||||
import ProductModule from '@medusajs/medusa/product';
|
||||
import { defineLink } from '@medusajs/framework/utils';
|
||||
|
||||
export default defineLink(
|
||||
ProductModule.linkable.product,
|
||||
HelloModule.linkable.myCustom,
|
||||
);
|
||||
export default defineLink(ProductModule.linkable.product, HelloModule.linkable.myCustom);
|
||||
```
|
||||
|
||||
This defines a link between the Product Module's `product` data model and the Hello Module (custom module)'s `myCustom` data model.
|
||||
|
||||
@ -13,7 +13,7 @@ For example, create the file `src/modules/hello/service.ts` with the following c
|
||||
```ts title="src/modules/hello/service.ts"
|
||||
export default class HelloModuleService {
|
||||
getMessage() {
|
||||
return "Hello, world!";
|
||||
return 'Hello, world!';
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -25,10 +25,10 @@ A module must have an `index.ts` file in its root directory that exports its def
|
||||
For example, create the file `src/modules/hello.index.ts` with the following content:
|
||||
|
||||
```ts title="src/modules/hello.index.ts" highlights={[["4", "", "The main service of the module."]]}
|
||||
import HelloModuleService from "./service";
|
||||
import { Module } from "@medusajs/framework/utils";
|
||||
import HelloModuleService from './service';
|
||||
import { Module } from '@medusajs/framework/utils';
|
||||
|
||||
export const HELLO_MODULE = "helloModuleService";
|
||||
export const HELLO_MODULE = 'helloModuleService';
|
||||
|
||||
export default Module(HELLO_MODULE, {
|
||||
service: HelloModuleService,
|
||||
@ -42,13 +42,13 @@ The last step is to add the module in Medusa’s configurations.
|
||||
In `medusa-config.js`, add the module to the `modules` object:
|
||||
|
||||
```js title="medusa-config.js"
|
||||
import { HELLO_MODULE } from "./src/modules/hello";
|
||||
import { HELLO_MODULE } from './src/modules/hello';
|
||||
|
||||
module.exports = defineConfig({
|
||||
// ...
|
||||
modules: {
|
||||
[HELLO_MODULE]: {
|
||||
resolve: "./modules/hello",
|
||||
resolve: './modules/hello',
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -61,16 +61,12 @@ Its key (`helloModuleService` or `HELLO_MODULE`) is the name of the module’s m
|
||||
You can resolve the main service of the module in other resources, such as an API route:
|
||||
|
||||
```ts
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework";
|
||||
import HelloModuleService from "../../../modules/hello/service";
|
||||
import { HELLO_MODULE } from "../../../modules/hello";
|
||||
import { MedusaRequest, MedusaResponse } from '@medusajs/framework';
|
||||
import HelloModuleService from '../../../modules/hello/service';
|
||||
import { HELLO_MODULE } from '../../../modules/hello';
|
||||
|
||||
export async function GET(
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse,
|
||||
): Promise<void> {
|
||||
const helloModuleService: HelloModuleService =
|
||||
req.scope.resolve(HELLO_MODULE);
|
||||
export async function GET(req: MedusaRequest, res: MedusaResponse): Promise<void> {
|
||||
const helloModuleService: HelloModuleService = req.scope.resolve(HELLO_MODULE);
|
||||
|
||||
res.json({
|
||||
message: helloModuleService.getMessage(),
|
||||
|
||||
6
src/modules/razorpay/index.ts
Normal file
6
src/modules/razorpay/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import RazorpayPaymentProvider from './service';
|
||||
import { ModuleProvider, Modules } from '@medusajs/framework/utils';
|
||||
|
||||
export default ModuleProvider(Modules.PAYMENT, {
|
||||
services: [RazorpayPaymentProvider],
|
||||
});
|
||||
371
src/modules/razorpay/service.ts
Normal file
371
src/modules/razorpay/service.ts
Normal file
@ -0,0 +1,371 @@
|
||||
import Razorpay from 'razorpay';
|
||||
// Adjust these imports to match your actual Medusa versions/paths
|
||||
import { AbstractPaymentProvider, BigNumber, MedusaError, PaymentSessionStatus } from '@medusajs/framework/utils';
|
||||
|
||||
import {
|
||||
CreatePaymentProviderSession,
|
||||
Logger,
|
||||
ProviderWebhookPayload,
|
||||
WebhookActionResult,
|
||||
} from '@medusajs/framework/types';
|
||||
|
||||
import {
|
||||
UpdatePaymentProviderSession,
|
||||
PaymentProviderError,
|
||||
PaymentProviderSessionResponse,
|
||||
} from '@medusajs/framework/types';
|
||||
|
||||
type RazorpayProviderOptions = {
|
||||
key_id: string;
|
||||
key_secret: string;
|
||||
// Add other config options if needed
|
||||
};
|
||||
|
||||
type InjectedDependencies = {
|
||||
logger: Logger;
|
||||
};
|
||||
|
||||
export class RazorpayPaymentProvider extends AbstractPaymentProvider<RazorpayProviderOptions> {
|
||||
protected logger_: Logger;
|
||||
protected options_: RazorpayProviderOptions;
|
||||
|
||||
// The unique identifier used by Medusa
|
||||
static identifier = 'razorpay';
|
||||
|
||||
protected razorpay: Razorpay;
|
||||
|
||||
constructor(container: InjectedDependencies, options: RazorpayProviderOptions) {
|
||||
super(container, options);
|
||||
this.logger_ = container.logger;
|
||||
this.options_ = options;
|
||||
|
||||
this.razorpay = new Razorpay({
|
||||
key_id: this.options_.key_id,
|
||||
key_secret: this.options_.key_secret,
|
||||
});
|
||||
}
|
||||
|
||||
static validateOptions(options: Record<any, any>) {
|
||||
if (!options.key_id) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, "Key ID is required in the provider's options.");
|
||||
}
|
||||
if (!options.key_secret) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, "Key Secret is required in the provider's options.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates a payment session (e.g. create an order in Razorpay).
|
||||
* Return a PaymentProviderSessionResponse:
|
||||
* { session_data: Record<string, unknown> }
|
||||
*/
|
||||
async initiatePayment(
|
||||
context: CreatePaymentProviderSession,
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse> {
|
||||
// Example: create an order in Razorpay
|
||||
const { currency_code, context: customerDetails } = context;
|
||||
try {
|
||||
const amount = (context.amount as number) * 100;
|
||||
// const currency = (data.currency as string) || "INR"
|
||||
const order = await this.razorpay.orders.create({
|
||||
amount,
|
||||
currency: currency_code.toUpperCase(),
|
||||
receipt: `receipt_${Date.now()}`,
|
||||
payment_capture: true, // auto-capture
|
||||
});
|
||||
|
||||
// Return a PaymentProviderSessionResponse
|
||||
return {
|
||||
...order,
|
||||
data: {
|
||||
id: order.id,
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return {
|
||||
error: e,
|
||||
code: 'unknown',
|
||||
detail: e,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the payment session data from Razorpay or from your local records.
|
||||
* Must return either session_data or throw a PaymentProviderError.
|
||||
*/
|
||||
async retrievePayment(
|
||||
paymentSessionData: Record<string, unknown>,
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse['data']> {
|
||||
const externalId = paymentSessionData.id;
|
||||
try {
|
||||
const razorpayOrderId = externalId as string;
|
||||
if (!razorpayOrderId) {
|
||||
throw new Error('No Razorpay order ID found.');
|
||||
}
|
||||
|
||||
// Example: fetch the order from Razorpay
|
||||
const order = await this.razorpay.orders.fetch(razorpayOrderId);
|
||||
|
||||
return {
|
||||
...order,
|
||||
status: order.status || 'created',
|
||||
id: externalId,
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
error: e,
|
||||
code: 'unknown',
|
||||
detail: e,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the payment session. The signature typically expects:
|
||||
* (context: UpdatePaymentProviderSession) => Promise<PaymentProviderError | PaymentProviderSessionResponse>
|
||||
*/
|
||||
async updatePayment(
|
||||
context: UpdatePaymentProviderSession,
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse> {
|
||||
// Deconstruct the data from context
|
||||
const { amount, currency_code, context: customerDetails, data } = context;
|
||||
const externalId = data.id;
|
||||
try {
|
||||
const response = await this.razorpay.payments.edit(externalId as string, {
|
||||
notes: {
|
||||
amount: amount as number,
|
||||
currency: currency_code,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
...response,
|
||||
data: {
|
||||
id: response.id,
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
error: e,
|
||||
code: 'unknown',
|
||||
detail: e,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize the payment (e.g., verify webhook signature, check that payment is successful).
|
||||
* Return updated session data or PaymentProviderError.
|
||||
*/
|
||||
async authorizePayment(
|
||||
paymentSessionData: Record<string, unknown>,
|
||||
context: Record<string, unknown>,
|
||||
): Promise<
|
||||
| PaymentProviderError
|
||||
| {
|
||||
status: PaymentSessionStatus;
|
||||
data: PaymentProviderSessionResponse['data'];
|
||||
}
|
||||
> {
|
||||
const externalId = paymentSessionData.id;
|
||||
try {
|
||||
console.log('authorizePayment externalId', externalId);
|
||||
// Here you could verify a signature or confirm the payment status in Razorpay
|
||||
const paymentData = await this.razorpay.orders.fetchPayments(externalId as string);
|
||||
// if the externalId is paymentId, then use this.razorpay.payments.fetch(externalId as string)
|
||||
// const paymentData = await this.razorpay.payments.fetch(externalId as string)
|
||||
return {
|
||||
data: {
|
||||
...paymentSessionData,
|
||||
id: externalId,
|
||||
},
|
||||
status: PaymentSessionStatus.AUTHORIZED,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
error: error,
|
||||
code: 'unknown',
|
||||
detail: error,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture the payment if not auto-captured.
|
||||
*/
|
||||
async capturePayment(
|
||||
paymentData: Record<string, unknown>,
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse['data']> {
|
||||
try {
|
||||
const externalId = paymentData.id;
|
||||
const { amount } = paymentData;
|
||||
// e.g. this.razorpay.payments.capture(paymentId, amount) if not auto-captured
|
||||
const newData = await this.razorpay.payments.capture(externalId as string, amount as number, 'INR');
|
||||
return {
|
||||
...newData,
|
||||
// status: "captured",
|
||||
id: externalId,
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
error: e,
|
||||
code: 'unknown',
|
||||
detail: e,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refund the payment in Razorpay.
|
||||
*/
|
||||
async refundPayment(
|
||||
paymentData: Record<string, unknown>,
|
||||
refundAmount: number,
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse['data']> {
|
||||
const externalId = paymentData.id;
|
||||
try {
|
||||
const newData = await this.razorpay.payments.refund(externalId as string, { amount: refundAmount });
|
||||
return {
|
||||
...newData,
|
||||
id: externalId,
|
||||
// refunded_amount: refundAmount,
|
||||
// status: "refunded",
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
error: e,
|
||||
code: 'unknown',
|
||||
detail: e,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a payment if it hasn't been paid. For Razorpay, you may just mark the order as canceled in your system.
|
||||
*/
|
||||
async cancelPayment(
|
||||
paymentData: Record<string, unknown>,
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse['data']> {
|
||||
try {
|
||||
const externalId = paymentData.id;
|
||||
return {
|
||||
id: externalId,
|
||||
status: 'canceled',
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
error: error,
|
||||
code: 'unknown',
|
||||
detail: error,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a payment session. Must return either updated session or PaymentProviderError.
|
||||
* The base signature often is:
|
||||
* (paymentSessionData: Record<string, unknown>) => Promise<Record<string, unknown> | PaymentProviderError>
|
||||
* or might require returning PaymentProviderSessionResponse. Adjust if needed.
|
||||
*/
|
||||
async deletePayment(
|
||||
paymentSessionData: Record<string, unknown>,
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse['data']> {
|
||||
const externalId = paymentSessionData.id;
|
||||
try {
|
||||
// If Razorpay does not allow deleting an order, just mark locally as deleted
|
||||
// Return the final state or an empty object
|
||||
return {
|
||||
id: externalId,
|
||||
deleted: true,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
error: error,
|
||||
code: 'unknown',
|
||||
detail: error,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the status (PaymentSessionStatus) for the payment.
|
||||
* Must return a PaymentSessionStatus enum value (e.g., "AUTHORIZED", "PENDING", "REQUIRES_MORE", "ERROR", etc.)
|
||||
*/
|
||||
async getPaymentStatus(paymentSessionData: Record<string, unknown>): Promise<PaymentSessionStatus> {
|
||||
// For a minimal example:
|
||||
const payment = await this.razorpay.payments.fetch(paymentSessionData.id as string);
|
||||
const status = payment.status;
|
||||
switch (status?.toLowerCase()) {
|
||||
case 'created':
|
||||
return PaymentSessionStatus.PENDING;
|
||||
case 'authorized':
|
||||
return PaymentSessionStatus.AUTHORIZED;
|
||||
case 'captured':
|
||||
return PaymentSessionStatus.CAPTURED;
|
||||
case 'refunded':
|
||||
return PaymentSessionStatus.CANCELED; // Payment was authorized before refund
|
||||
case 'failed':
|
||||
return PaymentSessionStatus.ERROR;
|
||||
default:
|
||||
return PaymentSessionStatus.ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle webhook events. The base signature often is:
|
||||
* getWebhookActionAndData(data: {
|
||||
* data: Record<string, unknown>;
|
||||
* rawData: string | Buffer<ArrayBufferLike>;
|
||||
* headers: Record<string, unknown>;
|
||||
* }): Promise<WebhookActionResult>
|
||||
*
|
||||
* Must return an object with:
|
||||
* { action: PaymentActions; data: Record<string, unknown> }
|
||||
*/
|
||||
async getWebhookActionAndData(
|
||||
// data: {
|
||||
// data: Record<string, unknown>
|
||||
// rawData: string | Buffer<ArrayBufferLike>
|
||||
// headers: Record<string, unknown>
|
||||
// }
|
||||
payload: ProviderWebhookPayload['payload'],
|
||||
): Promise<WebhookActionResult> {
|
||||
const { data, rawData, headers } = payload;
|
||||
try {
|
||||
switch (data.event_type) {
|
||||
case 'authorized_amount':
|
||||
return {
|
||||
action: 'authorized',
|
||||
data: {
|
||||
session_id: (data.metadata as Record<string, any>).session_id,
|
||||
amount: new BigNumber(data.amount as number),
|
||||
},
|
||||
};
|
||||
case 'success':
|
||||
return {
|
||||
action: 'captured',
|
||||
data: {
|
||||
session_id: (data.metadata as Record<string, any>).session_id,
|
||||
amount: new BigNumber(data.amount as number),
|
||||
},
|
||||
};
|
||||
default:
|
||||
return {
|
||||
action: 'not_supported',
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
return {
|
||||
action: 'failed',
|
||||
data: {
|
||||
session_id: (data.metadata as Record<string, any>).session_id,
|
||||
amount: new BigNumber(data.amount as number),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default RazorpayPaymentProvider;
|
||||
@ -9,13 +9,11 @@ To create a custom CLI script, create a TypeScript or JavaScript file under the
|
||||
For example, create the file `src/scripts/my-script.ts` with the following content:
|
||||
|
||||
```ts title="src/scripts/my-script.ts"
|
||||
import { ExecArgs, IProductModuleService } from "@medusajs/framework/types";
|
||||
import { Modules } from "@medusajs/framework/utils";
|
||||
import { ExecArgs, IProductModuleService } from '@medusajs/framework/types';
|
||||
import { Modules } from '@medusajs/framework/utils';
|
||||
|
||||
export default async function myScript({ container }: ExecArgs) {
|
||||
const productModuleService: IProductModuleService = container.resolve(
|
||||
Modules.PRODUCT,
|
||||
);
|
||||
const productModuleService: IProductModuleService = container.resolve(Modules.PRODUCT);
|
||||
|
||||
const [, count] = await productModuleService.listAndCountProducts();
|
||||
|
||||
@ -44,7 +42,7 @@ Your script can accept arguments from the command line. Arguments are passed to
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { ExecArgs } from "@medusajs/framework/types";
|
||||
import { ExecArgs } from '@medusajs/framework/types';
|
||||
|
||||
export default async function myScript({ args }: ExecArgs) {
|
||||
console.log(`The arguments you passed: ${args}`);
|
||||
|
||||
@ -12,13 +12,9 @@ import {
|
||||
linkSalesChannelsToApiKeyWorkflow,
|
||||
linkSalesChannelsToStockLocationWorkflow,
|
||||
updateStoresWorkflow,
|
||||
} from "@medusajs/medusa/core-flows";
|
||||
import { ExecArgs } from "@medusajs/framework/types";
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
Modules,
|
||||
ProductStatus,
|
||||
} from "@medusajs/framework/utils";
|
||||
} from '@medusajs/medusa/core-flows';
|
||||
import { ExecArgs } from '@medusajs/framework/types';
|
||||
import { ContainerRegistrationKeys, Modules, ProductStatus } from '@medusajs/framework/utils';
|
||||
|
||||
export default async function seedDemoData({ container }: ExecArgs) {
|
||||
const logger = container.resolve(ContainerRegistrationKeys.LOGGER);
|
||||
@ -28,23 +24,21 @@ export default async function seedDemoData({ container }: ExecArgs) {
|
||||
const salesChannelModuleService = container.resolve(Modules.SALES_CHANNEL);
|
||||
const storeModuleService = container.resolve(Modules.STORE);
|
||||
|
||||
const countries = ["gb", "de", "dk", "se", "fr", "es", "it"];
|
||||
const countries = ['gb', 'de', 'dk', 'se', 'fr', 'es', 'it'];
|
||||
|
||||
logger.info("Seeding store data...");
|
||||
logger.info('Seeding store data...');
|
||||
const [store] = await storeModuleService.listStores();
|
||||
let defaultSalesChannel = await salesChannelModuleService.listSalesChannels({
|
||||
name: "Default Sales Channel",
|
||||
name: 'Default Sales Channel',
|
||||
});
|
||||
|
||||
if (!defaultSalesChannel.length) {
|
||||
// create the default sales channel
|
||||
const { result: salesChannelResult } = await createSalesChannelsWorkflow(
|
||||
container,
|
||||
).run({
|
||||
const { result: salesChannelResult } = await createSalesChannelsWorkflow(container).run({
|
||||
input: {
|
||||
salesChannelsData: [
|
||||
{
|
||||
name: "Default Sales Channel",
|
||||
name: 'Default Sales Channel',
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -58,53 +52,51 @@ export default async function seedDemoData({ container }: ExecArgs) {
|
||||
update: {
|
||||
supported_currencies: [
|
||||
{
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
is_default: true,
|
||||
},
|
||||
{
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
default_sales_channel_id: defaultSalesChannel[0].id,
|
||||
},
|
||||
},
|
||||
});
|
||||
logger.info("Seeding region data...");
|
||||
logger.info('Seeding region data...');
|
||||
const { result: regionResult } = await createRegionsWorkflow(container).run({
|
||||
input: {
|
||||
regions: [
|
||||
{
|
||||
name: "Europe",
|
||||
currency_code: "eur",
|
||||
name: 'Europe',
|
||||
currency_code: 'eur',
|
||||
countries,
|
||||
payment_providers: ["pp_system_default"],
|
||||
payment_providers: ['pp_system_default'],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
const region = regionResult[0];
|
||||
logger.info("Finished seeding regions.");
|
||||
logger.info('Finished seeding regions.');
|
||||
|
||||
logger.info("Seeding tax regions...");
|
||||
logger.info('Seeding tax regions...');
|
||||
await createTaxRegionsWorkflow(container).run({
|
||||
input: countries.map((country_code) => ({
|
||||
country_code,
|
||||
})),
|
||||
});
|
||||
logger.info("Finished seeding tax regions.");
|
||||
logger.info('Finished seeding tax regions.');
|
||||
|
||||
logger.info("Seeding stock location data...");
|
||||
const { result: stockLocationResult } = await createStockLocationsWorkflow(
|
||||
container,
|
||||
).run({
|
||||
logger.info('Seeding stock location data...');
|
||||
const { result: stockLocationResult } = await createStockLocationsWorkflow(container).run({
|
||||
input: {
|
||||
locations: [
|
||||
{
|
||||
name: "European Warehouse",
|
||||
name: 'European Warehouse',
|
||||
address: {
|
||||
city: "Copenhagen",
|
||||
country_code: "DK",
|
||||
address_1: "",
|
||||
city: 'Copenhagen',
|
||||
country_code: 'DK',
|
||||
address_1: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -117,58 +109,57 @@ export default async function seedDemoData({ container }: ExecArgs) {
|
||||
stock_location_id: stockLocation.id,
|
||||
},
|
||||
[Modules.FULFILLMENT]: {
|
||||
fulfillment_provider_id: "manual_manual",
|
||||
fulfillment_provider_id: 'manual_manual',
|
||||
},
|
||||
});
|
||||
|
||||
logger.info("Seeding fulfillment data...");
|
||||
const { result: shippingProfileResult } =
|
||||
await createShippingProfilesWorkflow(container).run({
|
||||
input: {
|
||||
data: [
|
||||
{
|
||||
name: "Default",
|
||||
type: "default",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
logger.info('Seeding fulfillment data...');
|
||||
const { result: shippingProfileResult } = await createShippingProfilesWorkflow(container).run({
|
||||
input: {
|
||||
data: [
|
||||
{
|
||||
name: 'Default',
|
||||
type: 'default',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
const shippingProfile = shippingProfileResult[0];
|
||||
|
||||
const fulfillmentSet = await fulfillmentModuleService.createFulfillmentSets({
|
||||
name: "European Warehouse delivery",
|
||||
type: "shipping",
|
||||
name: 'European Warehouse delivery',
|
||||
type: 'shipping',
|
||||
service_zones: [
|
||||
{
|
||||
name: "Europe",
|
||||
name: 'Europe',
|
||||
geo_zones: [
|
||||
{
|
||||
country_code: "gb",
|
||||
type: "country",
|
||||
country_code: 'gb',
|
||||
type: 'country',
|
||||
},
|
||||
{
|
||||
country_code: "de",
|
||||
type: "country",
|
||||
country_code: 'de',
|
||||
type: 'country',
|
||||
},
|
||||
{
|
||||
country_code: "dk",
|
||||
type: "country",
|
||||
country_code: 'dk',
|
||||
type: 'country',
|
||||
},
|
||||
{
|
||||
country_code: "se",
|
||||
type: "country",
|
||||
country_code: 'se',
|
||||
type: 'country',
|
||||
},
|
||||
{
|
||||
country_code: "fr",
|
||||
type: "country",
|
||||
country_code: 'fr',
|
||||
type: 'country',
|
||||
},
|
||||
{
|
||||
country_code: "es",
|
||||
type: "country",
|
||||
country_code: 'es',
|
||||
type: 'country',
|
||||
},
|
||||
{
|
||||
country_code: "it",
|
||||
type: "country",
|
||||
country_code: 'it',
|
||||
type: 'country',
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -187,23 +178,23 @@ export default async function seedDemoData({ container }: ExecArgs) {
|
||||
await createShippingOptionsWorkflow(container).run({
|
||||
input: [
|
||||
{
|
||||
name: "Standard Shipping",
|
||||
price_type: "flat",
|
||||
provider_id: "manual_manual",
|
||||
name: 'Standard Shipping',
|
||||
price_type: 'flat',
|
||||
provider_id: 'manual_manual',
|
||||
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
type: {
|
||||
label: "Standard",
|
||||
description: "Ship in 2-3 days.",
|
||||
code: "standard",
|
||||
label: 'Standard',
|
||||
description: 'Ship in 2-3 days.',
|
||||
code: 'standard',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
amount: 10,
|
||||
},
|
||||
{
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
amount: 10,
|
||||
},
|
||||
{
|
||||
@ -213,35 +204,35 @@ export default async function seedDemoData({ container }: ExecArgs) {
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
attribute: "enabled_in_store",
|
||||
attribute: 'enabled_in_store',
|
||||
value: '"true"',
|
||||
operator: "eq",
|
||||
operator: 'eq',
|
||||
},
|
||||
{
|
||||
attribute: "is_return",
|
||||
value: "false",
|
||||
operator: "eq",
|
||||
attribute: 'is_return',
|
||||
value: 'false',
|
||||
operator: 'eq',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Express Shipping",
|
||||
price_type: "flat",
|
||||
provider_id: "manual_manual",
|
||||
name: 'Express Shipping',
|
||||
price_type: 'flat',
|
||||
provider_id: 'manual_manual',
|
||||
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
type: {
|
||||
label: "Express",
|
||||
description: "Ship in 24 hours.",
|
||||
code: "express",
|
||||
label: 'Express',
|
||||
description: 'Ship in 24 hours.',
|
||||
code: 'express',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
amount: 10,
|
||||
},
|
||||
{
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
amount: 10,
|
||||
},
|
||||
{
|
||||
@ -251,20 +242,20 @@ export default async function seedDemoData({ container }: ExecArgs) {
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
attribute: "enabled_in_store",
|
||||
attribute: 'enabled_in_store',
|
||||
value: '"true"',
|
||||
operator: "eq",
|
||||
operator: 'eq',
|
||||
},
|
||||
{
|
||||
attribute: "is_return",
|
||||
value: "false",
|
||||
operator: "eq",
|
||||
attribute: 'is_return',
|
||||
value: 'false',
|
||||
operator: 'eq',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
logger.info("Finished seeding fulfillment data.");
|
||||
logger.info('Finished seeding fulfillment data.');
|
||||
|
||||
await linkSalesChannelsToStockLocationWorkflow(container).run({
|
||||
input: {
|
||||
@ -272,18 +263,16 @@ export default async function seedDemoData({ container }: ExecArgs) {
|
||||
add: [defaultSalesChannel[0].id],
|
||||
},
|
||||
});
|
||||
logger.info("Finished seeding stock location data.");
|
||||
logger.info('Finished seeding stock location data.');
|
||||
|
||||
logger.info("Seeding publishable API key data...");
|
||||
const { result: publishableApiKeyResult } = await createApiKeysWorkflow(
|
||||
container,
|
||||
).run({
|
||||
logger.info('Seeding publishable API key data...');
|
||||
const { result: publishableApiKeyResult } = await createApiKeysWorkflow(container).run({
|
||||
input: {
|
||||
api_keys: [
|
||||
{
|
||||
title: "Webshop",
|
||||
type: "publishable",
|
||||
created_by: "",
|
||||
title: 'Webshop',
|
||||
type: 'publishable',
|
||||
created_by: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -296,29 +285,27 @@ export default async function seedDemoData({ container }: ExecArgs) {
|
||||
add: [defaultSalesChannel[0].id],
|
||||
},
|
||||
});
|
||||
logger.info("Finished seeding publishable API key data.");
|
||||
logger.info('Finished seeding publishable API key data.');
|
||||
|
||||
logger.info("Seeding product data...");
|
||||
logger.info('Seeding product data...');
|
||||
|
||||
const { result: categoryResult } = await createProductCategoriesWorkflow(
|
||||
container,
|
||||
).run({
|
||||
const { result: categoryResult } = await createProductCategoriesWorkflow(container).run({
|
||||
input: {
|
||||
product_categories: [
|
||||
{
|
||||
name: "Shirts",
|
||||
name: 'Shirts',
|
||||
is_active: true,
|
||||
},
|
||||
{
|
||||
name: "Sweatshirts",
|
||||
name: 'Sweatshirts',
|
||||
is_active: true,
|
||||
},
|
||||
{
|
||||
name: "Pants",
|
||||
name: 'Pants',
|
||||
is_active: true,
|
||||
},
|
||||
{
|
||||
name: "Merch",
|
||||
name: 'Merch',
|
||||
is_active: true,
|
||||
},
|
||||
],
|
||||
@ -329,181 +316,179 @@ export default async function seedDemoData({ container }: ExecArgs) {
|
||||
input: {
|
||||
products: [
|
||||
{
|
||||
title: "Medusa T-Shirt",
|
||||
category_ids: [
|
||||
categoryResult.find((cat) => cat.name === "Shirts").id,
|
||||
],
|
||||
title: 'Medusa T-Shirt',
|
||||
category_ids: [categoryResult.find((cat) => cat.name === 'Shirts').id],
|
||||
description:
|
||||
"Reimagine the feeling of a classic T-shirt. With our cotton T-shirts, everyday essentials no longer have to be ordinary.",
|
||||
handle: "t-shirt",
|
||||
'Reimagine the feeling of a classic T-shirt. With our cotton T-shirts, everyday essentials no longer have to be ordinary.',
|
||||
handle: 't-shirt',
|
||||
weight: 400,
|
||||
status: ProductStatus.PUBLISHED,
|
||||
images: [
|
||||
{
|
||||
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/tee-black-front.png",
|
||||
url: 'https://medusa-public-images.s3.eu-west-1.amazonaws.com/tee-black-front.png',
|
||||
},
|
||||
{
|
||||
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/tee-black-back.png",
|
||||
url: 'https://medusa-public-images.s3.eu-west-1.amazonaws.com/tee-black-back.png',
|
||||
},
|
||||
{
|
||||
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/tee-white-front.png",
|
||||
url: 'https://medusa-public-images.s3.eu-west-1.amazonaws.com/tee-white-front.png',
|
||||
},
|
||||
{
|
||||
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/tee-white-back.png",
|
||||
url: 'https://medusa-public-images.s3.eu-west-1.amazonaws.com/tee-white-back.png',
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
title: "Size",
|
||||
values: ["S", "M", "L", "XL"],
|
||||
title: 'Size',
|
||||
values: ['S', 'M', 'L', 'XL'],
|
||||
},
|
||||
{
|
||||
title: "Color",
|
||||
values: ["Black", "White"],
|
||||
title: 'Color',
|
||||
values: ['Black', 'White'],
|
||||
},
|
||||
],
|
||||
variants: [
|
||||
{
|
||||
title: "S / Black",
|
||||
sku: "SHIRT-S-BLACK",
|
||||
title: 'S / Black',
|
||||
sku: 'SHIRT-S-BLACK',
|
||||
options: {
|
||||
Size: "S",
|
||||
Color: "Black",
|
||||
Size: 'S',
|
||||
Color: 'Black',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "S / White",
|
||||
sku: "SHIRT-S-WHITE",
|
||||
title: 'S / White',
|
||||
sku: 'SHIRT-S-WHITE',
|
||||
options: {
|
||||
Size: "S",
|
||||
Color: "White",
|
||||
Size: 'S',
|
||||
Color: 'White',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "M / Black",
|
||||
sku: "SHIRT-M-BLACK",
|
||||
title: 'M / Black',
|
||||
sku: 'SHIRT-M-BLACK',
|
||||
options: {
|
||||
Size: "M",
|
||||
Color: "Black",
|
||||
Size: 'M',
|
||||
Color: 'Black',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "M / White",
|
||||
sku: "SHIRT-M-WHITE",
|
||||
title: 'M / White',
|
||||
sku: 'SHIRT-M-WHITE',
|
||||
options: {
|
||||
Size: "M",
|
||||
Color: "White",
|
||||
Size: 'M',
|
||||
Color: 'White',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "L / Black",
|
||||
sku: "SHIRT-L-BLACK",
|
||||
title: 'L / Black',
|
||||
sku: 'SHIRT-L-BLACK',
|
||||
options: {
|
||||
Size: "L",
|
||||
Color: "Black",
|
||||
Size: 'L',
|
||||
Color: 'Black',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "L / White",
|
||||
sku: "SHIRT-L-WHITE",
|
||||
title: 'L / White',
|
||||
sku: 'SHIRT-L-WHITE',
|
||||
options: {
|
||||
Size: "L",
|
||||
Color: "White",
|
||||
Size: 'L',
|
||||
Color: 'White',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "XL / Black",
|
||||
sku: "SHIRT-XL-BLACK",
|
||||
title: 'XL / Black',
|
||||
sku: 'SHIRT-XL-BLACK',
|
||||
options: {
|
||||
Size: "XL",
|
||||
Color: "Black",
|
||||
Size: 'XL',
|
||||
Color: 'Black',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "XL / White",
|
||||
sku: "SHIRT-XL-WHITE",
|
||||
title: 'XL / White',
|
||||
sku: 'SHIRT-XL-WHITE',
|
||||
options: {
|
||||
Size: "XL",
|
||||
Color: "White",
|
||||
Size: 'XL',
|
||||
Color: 'White',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -515,95 +500,93 @@ export default async function seedDemoData({ container }: ExecArgs) {
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Medusa Sweatshirt",
|
||||
category_ids: [
|
||||
categoryResult.find((cat) => cat.name === "Sweatshirts").id,
|
||||
],
|
||||
title: 'Medusa Sweatshirt',
|
||||
category_ids: [categoryResult.find((cat) => cat.name === 'Sweatshirts').id],
|
||||
description:
|
||||
"Reimagine the feeling of a classic sweatshirt. With our cotton sweatshirt, everyday essentials no longer have to be ordinary.",
|
||||
handle: "sweatshirt",
|
||||
'Reimagine the feeling of a classic sweatshirt. With our cotton sweatshirt, everyday essentials no longer have to be ordinary.',
|
||||
handle: 'sweatshirt',
|
||||
weight: 400,
|
||||
status: ProductStatus.PUBLISHED,
|
||||
images: [
|
||||
{
|
||||
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatshirt-vintage-front.png",
|
||||
url: 'https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatshirt-vintage-front.png',
|
||||
},
|
||||
{
|
||||
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatshirt-vintage-back.png",
|
||||
url: 'https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatshirt-vintage-back.png',
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
title: "Size",
|
||||
values: ["S", "M", "L", "XL"],
|
||||
title: 'Size',
|
||||
values: ['S', 'M', 'L', 'XL'],
|
||||
},
|
||||
],
|
||||
variants: [
|
||||
{
|
||||
title: "S",
|
||||
sku: "SWEATSHIRT-S",
|
||||
title: 'S',
|
||||
sku: 'SWEATSHIRT-S',
|
||||
options: {
|
||||
Size: "S",
|
||||
Size: 'S',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "M",
|
||||
sku: "SWEATSHIRT-M",
|
||||
title: 'M',
|
||||
sku: 'SWEATSHIRT-M',
|
||||
options: {
|
||||
Size: "M",
|
||||
Size: 'M',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "L",
|
||||
sku: "SWEATSHIRT-L",
|
||||
title: 'L',
|
||||
sku: 'SWEATSHIRT-L',
|
||||
options: {
|
||||
Size: "L",
|
||||
Size: 'L',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "XL",
|
||||
sku: "SWEATSHIRT-XL",
|
||||
title: 'XL',
|
||||
sku: 'SWEATSHIRT-XL',
|
||||
options: {
|
||||
Size: "XL",
|
||||
Size: 'XL',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -615,93 +598,93 @@ export default async function seedDemoData({ container }: ExecArgs) {
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Medusa Sweatpants",
|
||||
category_ids: [categoryResult.find((cat) => cat.name === "Pants").id],
|
||||
title: 'Medusa Sweatpants',
|
||||
category_ids: [categoryResult.find((cat) => cat.name === 'Pants').id],
|
||||
description:
|
||||
"Reimagine the feeling of classic sweatpants. With our cotton sweatpants, everyday essentials no longer have to be ordinary.",
|
||||
handle: "sweatpants",
|
||||
'Reimagine the feeling of classic sweatpants. With our cotton sweatpants, everyday essentials no longer have to be ordinary.',
|
||||
handle: 'sweatpants',
|
||||
weight: 400,
|
||||
status: ProductStatus.PUBLISHED,
|
||||
images: [
|
||||
{
|
||||
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatpants-gray-front.png",
|
||||
url: 'https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatpants-gray-front.png',
|
||||
},
|
||||
{
|
||||
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatpants-gray-back.png",
|
||||
url: 'https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatpants-gray-back.png',
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
title: "Size",
|
||||
values: ["S", "M", "L", "XL"],
|
||||
title: 'Size',
|
||||
values: ['S', 'M', 'L', 'XL'],
|
||||
},
|
||||
],
|
||||
variants: [
|
||||
{
|
||||
title: "S",
|
||||
sku: "SWEATPANTS-S",
|
||||
title: 'S',
|
||||
sku: 'SWEATPANTS-S',
|
||||
options: {
|
||||
Size: "S",
|
||||
Size: 'S',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "M",
|
||||
sku: "SWEATPANTS-M",
|
||||
title: 'M',
|
||||
sku: 'SWEATPANTS-M',
|
||||
options: {
|
||||
Size: "M",
|
||||
Size: 'M',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "L",
|
||||
sku: "SWEATPANTS-L",
|
||||
title: 'L',
|
||||
sku: 'SWEATPANTS-L',
|
||||
options: {
|
||||
Size: "L",
|
||||
Size: 'L',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "XL",
|
||||
sku: "SWEATPANTS-XL",
|
||||
title: 'XL',
|
||||
sku: 'SWEATPANTS-XL',
|
||||
options: {
|
||||
Size: "XL",
|
||||
Size: 'XL',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -713,93 +696,93 @@ export default async function seedDemoData({ container }: ExecArgs) {
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Medusa Shorts",
|
||||
category_ids: [categoryResult.find((cat) => cat.name === "Merch").id],
|
||||
title: 'Medusa Shorts',
|
||||
category_ids: [categoryResult.find((cat) => cat.name === 'Merch').id],
|
||||
description:
|
||||
"Reimagine the feeling of classic shorts. With our cotton shorts, everyday essentials no longer have to be ordinary.",
|
||||
handle: "shorts",
|
||||
'Reimagine the feeling of classic shorts. With our cotton shorts, everyday essentials no longer have to be ordinary.',
|
||||
handle: 'shorts',
|
||||
weight: 400,
|
||||
status: ProductStatus.PUBLISHED,
|
||||
images: [
|
||||
{
|
||||
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/shorts-vintage-front.png",
|
||||
url: 'https://medusa-public-images.s3.eu-west-1.amazonaws.com/shorts-vintage-front.png',
|
||||
},
|
||||
{
|
||||
url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/shorts-vintage-back.png",
|
||||
url: 'https://medusa-public-images.s3.eu-west-1.amazonaws.com/shorts-vintage-back.png',
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
title: "Size",
|
||||
values: ["S", "M", "L", "XL"],
|
||||
title: 'Size',
|
||||
values: ['S', 'M', 'L', 'XL'],
|
||||
},
|
||||
],
|
||||
variants: [
|
||||
{
|
||||
title: "S",
|
||||
sku: "SHORTS-S",
|
||||
title: 'S',
|
||||
sku: 'SHORTS-S',
|
||||
options: {
|
||||
Size: "S",
|
||||
Size: 'S',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "M",
|
||||
sku: "SHORTS-M",
|
||||
title: 'M',
|
||||
sku: 'SHORTS-M',
|
||||
options: {
|
||||
Size: "M",
|
||||
Size: 'M',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "L",
|
||||
sku: "SHORTS-L",
|
||||
title: 'L',
|
||||
sku: 'SHORTS-L',
|
||||
options: {
|
||||
Size: "L",
|
||||
Size: 'L',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "XL",
|
||||
sku: "SHORTS-XL",
|
||||
title: 'XL',
|
||||
sku: 'SHORTS-XL',
|
||||
options: {
|
||||
Size: "XL",
|
||||
Size: 'XL',
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 10,
|
||||
currency_code: "eur",
|
||||
currency_code: 'eur',
|
||||
},
|
||||
{
|
||||
amount: 15,
|
||||
currency_code: "usd",
|
||||
currency_code: 'usd',
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -813,13 +796,13 @@ export default async function seedDemoData({ container }: ExecArgs) {
|
||||
],
|
||||
},
|
||||
});
|
||||
logger.info("Finished seeding product data.");
|
||||
logger.info('Finished seeding product data.');
|
||||
|
||||
logger.info("Seeding inventory levels.");
|
||||
logger.info('Seeding inventory levels.');
|
||||
|
||||
const { data: inventoryItems } = await query.graph({
|
||||
entity: "inventory_item",
|
||||
fields: ["id"],
|
||||
entity: 'inventory_item',
|
||||
fields: ['id'],
|
||||
});
|
||||
|
||||
const inventoryLevels = [];
|
||||
@ -838,5 +821,5 @@ export default async function seedDemoData({ container }: ExecArgs) {
|
||||
},
|
||||
});
|
||||
|
||||
logger.info("Finished seeding inventory levels data.");
|
||||
logger.info('Finished seeding inventory levels data.');
|
||||
}
|
||||
|
||||
@ -7,16 +7,16 @@ The subscriber is created in a TypeScript or JavaScript file under the `src/subs
|
||||
For example, create the file `src/subscribers/product-created.ts` with the following content:
|
||||
|
||||
```ts
|
||||
import { type SubscriberConfig } from "@medusajs/framework";
|
||||
import { type SubscriberConfig } from '@medusajs/framework';
|
||||
|
||||
// subscriber function
|
||||
export default async function productCreateHandler() {
|
||||
console.log("A product was created");
|
||||
console.log('A product was created');
|
||||
}
|
||||
|
||||
// subscriber config
|
||||
export const config: SubscriberConfig = {
|
||||
event: "product.created",
|
||||
event: 'product.created',
|
||||
};
|
||||
```
|
||||
|
||||
@ -33,19 +33,14 @@ A subscriber receives an object having the following properties:
|
||||
- `container`: The Medusa container. Use it to resolve modules' main services and other registered resources.
|
||||
|
||||
```ts
|
||||
import type { SubscriberArgs, SubscriberConfig } from "@medusajs/framework";
|
||||
import { IProductModuleService } from "@medusajs/framework/types";
|
||||
import { Modules } from "@medusajs/framework/utils";
|
||||
import type { SubscriberArgs, SubscriberConfig } from '@medusajs/framework';
|
||||
import { IProductModuleService } from '@medusajs/framework/types';
|
||||
import { Modules } from '@medusajs/framework/utils';
|
||||
|
||||
export default async function productCreateHandler({
|
||||
event: { data },
|
||||
container,
|
||||
}: SubscriberArgs<{ id: string }>) {
|
||||
export default async function productCreateHandler({ event: { data }, container }: SubscriberArgs<{ id: string }>) {
|
||||
const productId = data.id;
|
||||
|
||||
const productModuleService: IProductModuleService = container.resolve(
|
||||
Modules.PRODUCT,
|
||||
);
|
||||
const productModuleService: IProductModuleService = container.resolve(Modules.PRODUCT);
|
||||
|
||||
const product = await productModuleService.retrieveProduct(productId);
|
||||
|
||||
@ -53,6 +48,6 @@ export default async function productCreateHandler({
|
||||
}
|
||||
|
||||
export const config: SubscriberConfig = {
|
||||
event: "product.created",
|
||||
event: 'product.created',
|
||||
};
|
||||
```
|
||||
|
||||
@ -7,13 +7,9 @@ The workflow is created in a TypeScript or JavaScript file under the `src/workfl
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import {
|
||||
createStep,
|
||||
createWorkflow,
|
||||
StepResponse,
|
||||
} from "@medusajs/framework/workflows-sdk";
|
||||
import { createStep, createWorkflow, StepResponse } from '@medusajs/framework/workflows-sdk';
|
||||
|
||||
const step1 = createStep("step-1", async () => {
|
||||
const step1 = createStep('step-1', async () => {
|
||||
return new StepResponse(`Hello from step one!`);
|
||||
});
|
||||
|
||||
@ -21,7 +17,7 @@ type WorkflowInput = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
const step2 = createStep("step-2", async ({ name }: WorkflowInput) => {
|
||||
const step2 = createStep('step-2', async ({ name }: WorkflowInput) => {
|
||||
return new StepResponse(`Hello ${name} from step two!`);
|
||||
});
|
||||
|
||||
@ -29,18 +25,15 @@ type WorkflowOutput = {
|
||||
message: string;
|
||||
};
|
||||
|
||||
const myWorkflow = createWorkflow<WorkflowInput, WorkflowOutput>(
|
||||
"hello-world",
|
||||
function (input) {
|
||||
const str1 = step1();
|
||||
// to pass input
|
||||
step2(input);
|
||||
const myWorkflow = createWorkflow<WorkflowInput, WorkflowOutput>('hello-world', function (input) {
|
||||
const str1 = step1();
|
||||
// to pass input
|
||||
step2(input);
|
||||
|
||||
return {
|
||||
message: str1,
|
||||
};
|
||||
},
|
||||
);
|
||||
return {
|
||||
message: str1,
|
||||
};
|
||||
});
|
||||
|
||||
export default myWorkflow;
|
||||
```
|
||||
@ -52,8 +45,8 @@ You can execute the workflow from other resources, such as API routes, scheduled
|
||||
For example, to execute the workflow in an API route:
|
||||
|
||||
```ts
|
||||
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework";
|
||||
import myWorkflow from "../../../workflows/hello-world";
|
||||
import type { MedusaRequest, MedusaResponse } from '@medusajs/framework';
|
||||
import myWorkflow from '../../../workflows/hello-world';
|
||||
|
||||
export async function GET(req: MedusaRequest, res: MedusaResponse) {
|
||||
const { result } = await myWorkflow(req.scope).run({
|
||||
|
||||
14
yarn.lock
14
yarn.lock
@ -5699,7 +5699,7 @@ axios@^0.21.4:
|
||||
dependencies:
|
||||
follow-redirects "^1.14.0"
|
||||
|
||||
axios@^1.7.4:
|
||||
axios@^1.6.8, axios@^1.7.4:
|
||||
version "1.7.9"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.9.tgz#d7d071380c132a24accda1b2cfc1535b79ec650a"
|
||||
integrity sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==
|
||||
@ -9223,6 +9223,11 @@ postgres-interval@^1.1.0:
|
||||
dependencies:
|
||||
xtend "^4.0.0"
|
||||
|
||||
prettier@^3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.0.tgz#50325a28887c6dfdf2ca3f8eaba02b66a8429ca7"
|
||||
integrity sha512-quyMrVt6svPS7CjQ9gKb3GLEX/rl3BCL2oa/QkNcXv4YNVBC9olt3s+H7ukto06q7B1Qz46PbrKLO34PR6vXcA==
|
||||
|
||||
pretty-format@^29.0.0, pretty-format@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812"
|
||||
@ -9350,6 +9355,13 @@ raw-body@2.5.2:
|
||||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
razorpay@^2.9.5:
|
||||
version "2.9.5"
|
||||
resolved "https://registry.yarnpkg.com/razorpay/-/razorpay-2.9.5.tgz#6f0b1e31e09f796a360503fc4aebd46501a7e85f"
|
||||
integrity sha512-bmybwyszgfbYWAdO4igyHFk5zFj/D4YuoZAFNbyIYnPwzd+FBY5WvtpfUA9lVBMgnV4NzVEhncxR3It9RI/gCQ==
|
||||
dependencies:
|
||||
axios "^1.6.8"
|
||||
|
||||
react-aria@^3.33.1:
|
||||
version "3.36.0"
|
||||
resolved "https://registry.yarnpkg.com/react-aria/-/react-aria-3.36.0.tgz#95a8e3340ab400bfec4d159e47da8861469e5bcd"
|
||||
|
||||
Reference in New Issue
Block a user