Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core-flows,payment,medusa,types): Refund reasons management API #8436

Merged
merged 11 commits into from
Aug 6, 2024
16 changes: 12 additions & 4 deletions integration-tests/http/__tests__/payment/admin/payment.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,17 @@ medusaIntegrationTestRunner({
adminHeaders
)

// refund
const refundReason = (
await api.post(`/admin/refund-reasons`, { label: "test" }, adminHeaders)
).data.refund_reason

// BREAKING: reason is now refund_reason_id
const response = await api.post(
`/admin/payments/${payment.id}/refund`,
{
amount: 500,
// BREAKING: We should probably introduce reason and notes in V2 too
// reason: "return",
// note: "Do not like it",
refund_reason_id: refundReason.id,
note: "Do not like it",
},
adminHeaders
)
Expand All @@ -155,6 +158,11 @@ medusaIntegrationTestRunner({
expect.objectContaining({
id: expect.any(String),
amount: 500,
note: "Do not like it",
refund_reason_id: refundReason.id,
refund_reason: expect.objectContaining({
label: "test",
}),
}),
],
amount: 1000,
Expand Down
155 changes: 155 additions & 0 deletions integration-tests/http/__tests__/refund-reason/refund-reason.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { medusaIntegrationTestRunner } from "medusa-test-utils"
import {
adminHeaders,
createAdminUser,
} from "../../../helpers/create-admin-user"

jest.setTimeout(30000)

medusaIntegrationTestRunner({
testSuite: ({ dbConnection, api, getContainer }) => {
let refundReason1
let refundReason2

beforeEach(async () => {
const appContainer = getContainer()
await createAdminUser(dbConnection, adminHeaders, appContainer)

refundReason1 = (
await api.post(
"/admin/refund-reasons",
{ label: "reason 1 - too big" },
adminHeaders
)
).data.refund_reason

refundReason2 = (
await api.post(
"/admin/refund-reasons",
{ label: "reason 2 - too small" },
adminHeaders
)
).data.refund_reason
})

describe("GET /admin/refund-reasons", () => {
it("should list refund reasons and query count", async () => {
const response = await api
.get("/admin/refund-reasons", adminHeaders)
.catch((err) => {
console.log(err)
})

expect(response.status).toEqual(200)
expect(response.data.count).toEqual(2)
expect(response.data.refund_reasons).toEqual([
expect.objectContaining({
label: "reason 1 - too big",
}),
expect.objectContaining({
label: "reason 2 - too small",
}),
])
})

it("should list refund-reasons with specific query", async () => {
const response = await api.get(
"/admin/refund-reasons?q=1",
adminHeaders
)

expect(response.status).toEqual(200)
expect(response.data.count).toEqual(1)
expect(response.data.refund_reasons).toEqual(
expect.arrayContaining([
expect.objectContaining({
label: "reason 1 - too big",
}),
])
)
})
})

describe("POST /admin/refund-reasons", () => {
it("should create a refund reason", async () => {
const response = await api.post(
"/admin/refund-reasons",
{
label: "reason test",
description: "test description",
},
adminHeaders
)

expect(response.status).toEqual(200)
expect(response.data.refund_reason).toEqual(
expect.objectContaining({
label: "reason test",
description: "test description",
})
)
})
})

describe("POST /admin/refund-reasons/:id", () => {
it("should correctly update refund reason", async () => {
const response = await api.post(
`/admin/refund-reasons/${refundReason1.id}`,
{
label: "reason test",
description: "test description",
},
adminHeaders
)

expect(response.status).toEqual(200)
expect(response.data.refund_reason).toEqual(
expect.objectContaining({
label: "reason test",
description: "test description",
})
)
})
})

describe("GET /admin/refund-reasons/:id", () => {
it("should fetch a refund reason", async () => {
const response = await api.get(
`/admin/refund-reasons/${refundReason1.id}`,
adminHeaders
)

expect(response.status).toEqual(200)
expect(response.data.refund_reason).toEqual(
expect.objectContaining({
id: refundReason1.id,
})
)
})
})

describe("DELETE /admin/refund-reasons/:id", () => {
it("should remove refund reasons", async () => {
const deleteResponse = await api.delete(
`/admin/refund-reasons/${refundReason1.id}`,
adminHeaders
)

expect(deleteResponse.data).toEqual({
id: refundReason1.id,
object: "refund_reason",
deleted: true,
})

await api
.get(`/admin/refund-reasons/${refundReason1.id}`, adminHeaders)
.catch((error) => {
expect(error.response.data.type).toEqual("not_found")
expect(error.response.data.message).toEqual(
`Refund reason with id: ${refundReason1.id.id} not found`
)
})
})
})
},
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { CreateRefundReasonDTO, IPaymentModuleService } from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/utils"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"

export const createRefundReasonStepId = "create-refund-reason"
export const createRefundReasonStep = createStep(
createRefundReasonStepId,
async (data: CreateRefundReasonDTO[], { container }) => {
const service = container.resolve<IPaymentModuleService>(
ModuleRegistrationName.PAYMENT
)

const refundReasons = await service.createRefundReasons(data)

return new StepResponse(
refundReasons,
refundReasons.map((rr) => rr.id)
)
},
async (ids, { container }) => {
if (!ids?.length) {
return
}

const service = container.resolve<IPaymentModuleService>(
ModuleRegistrationName.PAYMENT
)

await service.deleteRefundReasons(ids)
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { IPaymentModuleService } from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/utils"
import { createStep, StepResponse } from "@medusajs/workflows-sdk"

export const deleteRefundReasonsStepId = "delete-refund-reasons"
export const deleteRefundReasonsStep = createStep(
deleteRefundReasonsStepId,
async (ids: string[], { container }) => {
const service = container.resolve<IPaymentModuleService>(
ModuleRegistrationName.PAYMENT
)

await service.softDeleteRefundReasons(ids)

return new StepResponse(void 0, ids)
},
async (prevCustomerIds, { container }) => {
if (!prevCustomerIds?.length) {
return
}

const service = container.resolve<IPaymentModuleService>(
ModuleRegistrationName.PAYMENT
)

await service.restoreRefundReasons(prevCustomerIds)
}
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export * from "./create-payment-session"
export * from "./create-refund-reasons"
export * from "./delete-payment-sessions"
export * from "./delete-refund-reasons"
export * from "./update-payment-collection"
export * from "./update-refund-reasons"
export * from "./validate-deleted-payment-sessions"
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { IPaymentModuleService, UpdateRefundReasonDTO } from "@medusajs/types"
import {
ModuleRegistrationName,
getSelectsAndRelationsFromObjectArray,
promiseAll,
} from "@medusajs/utils"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"

export const updateRefundReasonStepId = "update-refund-reasons"
export const updateRefundReasonsStep = createStep(
updateRefundReasonStepId,
async (data: UpdateRefundReasonDTO[], { container }) => {
const ids = data.map((d) => d.id)
const { selects, relations } = getSelectsAndRelationsFromObjectArray(data)
const service = container.resolve<IPaymentModuleService>(
ModuleRegistrationName.PAYMENT
)

const prevRefundReasons = await service.listRefundReasons(
{ id: ids },
{ select: selects, relations }
)

const reasons = await service.updateRefundReasons(data)

return new StepResponse(reasons, prevRefundReasons)
},
async (previousData, { container }) => {
if (!previousData) {
return
}

const service = container.resolve<IPaymentModuleService>(
ModuleRegistrationName.PAYMENT
)

await promiseAll(
previousData.map((refundReason) =>
service.updateRefundReasons(refundReason)
)
)
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { CreateRefundReasonDTO, RefundReasonDTO } from "@medusajs/types"
import {
WorkflowData,
WorkflowResponse,
createWorkflow,
} from "@medusajs/workflows-sdk"
import { createRefundReasonStep } from "../steps/create-refund-reasons"

export const createRefundReasonsWorkflowId = "create-refund-reasons-workflow"
export const createRefundReasonsWorkflow = createWorkflow(
createRefundReasonsWorkflowId,
(
input: WorkflowData<{ data: CreateRefundReasonDTO[] }>
): WorkflowResponse<RefundReasonDTO[]> => {
return new WorkflowResponse(createRefundReasonStep(input.data))
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {
WorkflowData,
WorkflowResponse,
createWorkflow,
} from "@medusajs/workflows-sdk"
import { deleteRefundReasonsStep } from "../steps"

export const deleteRefundReasonsWorkflowId = "delete-refund-reasons-workflow"
export const deleteRefundReasonsWorkflow = createWorkflow(
deleteRefundReasonsWorkflowId,
(input: WorkflowData<{ ids: string[] }>): WorkflowResponse<void> => {
return new WorkflowResponse(deleteRefundReasonsStep(input.ids))
}
)
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export * from "./create-payment-session"
export * from "./create-refund-reasons"
export * from "./update-refund-reasons"
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { RefundReasonDTO, UpdateRefundReasonDTO } from "@medusajs/types"
import {
WorkflowData,
WorkflowResponse,
createWorkflow,
} from "@medusajs/workflows-sdk"
import { updateRefundReasonsStep } from "../steps"

export const updateRefundReasonsWorkflowId = "update-refund-reasons"
export const updateRefundReasonsWorkflow = createWorkflow(
updateRefundReasonsWorkflowId,
(
input: WorkflowData<UpdateRefundReasonDTO[]>
): WorkflowResponse<RefundReasonDTO[]> => {
return new WorkflowResponse(updateRefundReasonsStep(input))
}
)
22 changes: 22 additions & 0 deletions packages/core/types/src/http/payment/admin.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { BaseFilterable } from "../../dal"
import {
BasePayment,
BasePaymentCollection,
Expand All @@ -7,6 +8,7 @@ import {
BasePaymentProviderFilters,
BasePaymentSession,
BasePaymentSessionFilters,
RefundReason,
} from "./common"

export interface AdminPaymentProvider extends BasePaymentProvider {
Expand Down Expand Up @@ -42,3 +44,23 @@ export interface AdminPaymentsResponse {
}

export interface AdminPaymentFilters extends BasePaymentFilters {}

// Refund reason

export interface AdminRefundReason extends RefundReason {}
export interface RefundReasonFilters extends BaseFilterable<AdminRefundReason> {
id?: string | string[]
}

export interface RefundReasonResponse {
refund_reason: AdminRefundReason
}

export interface RefundReasonsResponse {
refund_reasons: AdminRefundReason[]
}

export interface AdminCreateRefundReason {
label: string
description?: string
}
Loading
Loading