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

* feat(core-flows,payment,medusa,types): add ability to set and manage refund reasons

* fix(payment): validate total amount when refunding payment (#8437)

Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com>

* feature: introduce additional_data to the product endpoints (#8405)

* chore(docs): Generated References (#8440)

Generated the following references:
- `product`

* chore: align payment database schema

* Update packages/core/core-flows/src/payment-collection/steps/create-refund-reasons.ts

Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>

* chore: address review

---------

Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com>
Co-authored-by: Harminder Virk <virk.officials@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
Riqwan Thamir
2024-08-06 11:47:42 +02:00
committed by GitHub
parent 8fb079786d
commit 0ff5b975e7
36 changed files with 2412 additions and 9 deletions

View File

@@ -23,6 +23,7 @@ moduleIntegrationTestRunner<IPaymentModuleService>({
"payment",
"paymentCollection",
"paymentProvider",
"refundReason",
])
Object.keys(linkable).forEach((key) => {
@@ -54,6 +55,14 @@ moduleIntegrationTestRunner<IPaymentModuleService>({
field: "paymentProvider",
},
},
refundReason: {
id: {
linkable: "refund_reason_id",
primaryKey: "id",
serviceName: "payment",
field: "refundReason",
},
},
})
})

View File

@@ -4,13 +4,21 @@ import {
PaymentCollection,
PaymentProvider,
PaymentSession,
RefundReason,
} from "@models"
export const joinerConfig = defineJoinerConfig(Modules.PAYMENT, {
models: [Payment, PaymentCollection, PaymentProvider, PaymentSession],
models: [
Payment,
PaymentCollection,
PaymentProvider,
PaymentSession,
RefundReason,
],
linkableKeys: {
payment_id: Payment.name,
payment_collection_id: PaymentCollection.name,
payment_provider_id: PaymentProvider.name,
refund_reason_id: RefundReason.name,
},
})

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,81 @@
import { Migration } from "@mikro-orm/migrations"
export class Migration20240806072619 extends Migration {
async up(): Promise<void> {
this.addSql(
'create table if not exists "refund_reason" ("id" text not null, "label" text not null, "description" text null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "refund_reason_pkey" primary key ("id"));'
)
this.addSql(
'alter table if exists "payment_session" drop constraint if exists "payment_session_status_check";'
)
this.addSql(
'alter table if exists "payment_session" drop constraint if exists "payment_session_payment_collection_id_foreign";'
)
this.addSql(
'alter table if exists "payment_session" alter column "status" type text using ("status"::text);'
)
this.addSql(
"alter table if exists \"payment_session\" add constraint \"payment_session_status_check\" check (\"status\" in ('authorized', 'captured', 'pending', 'requires_more', 'error', 'canceled'));"
)
this.addSql(
'create index if not exists "IDX_payment_session_deleted_at" on "payment_session" ("deleted_at");'
)
this.addSql('drop index if exists "IDX_capture_deleted_at";')
this.addSql('drop index if exists "IDX_refund_deleted_at";')
this.addSql(
'create index if not exists "IDX_payment_payment_session_id" on "payment" ("payment_session_id");'
)
this.addSql(
'alter table if exists "payment" add constraint "payment_payment_session_id_unique" unique ("payment_session_id");'
)
this.addSql(
'create index if not exists "IDX_capture_deleted_at" on "capture" ("deleted_at");'
)
this.addSql(
'alter table if exists "refund" add column if not exists "refund_reason_id" text null, add column if not exists "note" text null;'
)
this.addSql(
'create index if not exists "IDX_refund_deleted_at" on "refund" ("deleted_at");'
)
}
async down(): Promise<void> {
this.addSql('drop table if exists "refund_reason" cascade;')
this.addSql(
'alter table if exists "payment_session" drop constraint if exists "payment_session_status_check";'
)
this.addSql('drop index if exists "IDX_capture_deleted_at";')
this.addSql('drop index if exists "IDX_payment_payment_session_id";')
this.addSql(
'alter table if exists "payment" drop constraint if exists "payment_payment_session_id_unique";'
)
this.addSql(
'create index if not exists "IDX_capture_deleted_at" on "payment" ("deleted_at");'
)
this.addSql(
'create index if not exists "IDX_refund_deleted_at" on "payment" ("deleted_at");'
)
this.addSql(
'alter table if exists "payment_session" alter column "status" type text using ("status"::text);'
)
this.addSql(
"alter table if exists \"payment_session\" add constraint \"payment_session_status_check\" check (\"status\" in ('authorized', 'pending', 'requires_more', 'error', 'canceled'));"
)
this.addSql('drop index if exists "IDX_payment_session_deleted_at";')
this.addSql('drop index if exists "IDX_refund_deleted_at";')
this.addSql(
'alter table if exists "refund" drop column if exists "refund_reason_id";'
)
this.addSql('alter table if exists "refund" drop column if exists "note";')
}
}

View File

@@ -37,6 +37,9 @@ export default class Capture {
})
payment!: Payment
@Property({ columnType: "jsonb", nullable: true })
metadata: Record<string, unknown> | null = null
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",

View File

@@ -5,3 +5,4 @@ export { default as PaymentMethodToken } from "./payment-method-token"
export { default as PaymentProvider } from "./payment-provider"
export { default as PaymentSession } from "./payment-session"
export { default as Refund } from "./refund"
export { default as RefundReason } from "./refund-reason"

View File

@@ -45,7 +45,7 @@ export default class PaymentMethodToken {
@Property({
columnType: "timestamptz",
nullable: true,
index: "IDX_payment_metod_token_deleted_at",
index: "IDX_payment_method_token_deleted_at",
})
deleted_at: Date | null = null

View File

@@ -79,6 +79,9 @@ export default class PaymentSession {
})
payment?: Rel<Payment> | null
@Property({ columnType: "jsonb", nullable: true })
metadata: Record<string, unknown> | null = null
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",

View File

@@ -4,6 +4,7 @@ import {
DALUtils,
MikroOrmBigNumberProperty,
Searchable,
createPsqlIndexStatementHelper,
generateEntityId,
} from "@medusajs/utils"
import {
@@ -28,7 +29,13 @@ import Refund from "./refund"
type OptionalPaymentProps = DAL.ModelDateColumns
@Entity({ tableName: "payment" })
const tableName = "payment"
const ProviderIdIndex = createPsqlIndexStatementHelper({
tableName,
columns: "provider_id",
})
@Entity({ tableName })
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class Payment {
[OptionalProps]?: OptionalPaymentProps
@@ -46,6 +53,7 @@ export default class Payment {
currency_code: string
@Property({ columnType: "text" })
@ProviderIdIndex.MikroORMIndex()
provider_id: string
@Searchable()

View File

@@ -0,0 +1,54 @@
import { DALUtils, generateEntityId, Searchable } from "@medusajs/utils"
import {
BeforeCreate,
Entity,
Filter,
OnInit,
PrimaryKey,
Property,
} from "@mikro-orm/core"
@Entity({ tableName: "refund_reason" })
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class RefundReason {
@PrimaryKey({ columnType: "text" })
id: string
@Searchable()
@Property({ columnType: "text" })
label: string
@Property({ columnType: "text", nullable: true })
description: string | null = null
@Property({ columnType: "jsonb", nullable: true })
metadata: Record<string, unknown> | null = null
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
created_at: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at: Date
@Property({ columnType: "timestamptz", nullable: true })
deleted_at: Date | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "refr")
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "refr")
}
}

View File

@@ -1,4 +1,4 @@
import { BigNumberRawValue } from "@medusajs/types"
import { BigNumberRawValue, DAL } from "@medusajs/types"
import {
BigNumber,
MikroOrmBigNumberProperty,
@@ -9,14 +9,24 @@ import {
Entity,
ManyToOne,
OnInit,
OptionalProps,
PrimaryKey,
Property,
Rel,
} from "@mikro-orm/core"
import Payment from "./payment"
import RefundReason from "./refund-reason"
type OptionalProps =
| "note"
| "refund_reason_id"
| "refund_reason"
| DAL.ModelDateColumns
@Entity({ tableName: "refund" })
export default class Refund {
[OptionalProps]?: OptionalProps
@PrimaryKey({ columnType: "text" })
id: string
@@ -36,6 +46,20 @@ export default class Refund {
@Property({ columnType: "text", nullable: true })
payment_id: string
@ManyToOne(() => RefundReason, {
columnType: "text",
mapToPk: true,
fieldName: "refund_reason_id",
nullable: true,
})
refund_reason_id: string | null = null
@ManyToOne(() => RefundReason, { persist: false, nullable: true })
refund_reason: Rel<RefundReason> | null = null
@Property({ columnType: "text", nullable: true })
note: string | null = null
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
@@ -66,10 +90,12 @@ export default class Refund {
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "ref")
this.refund_reason_id ??= this.refund_reason?.id || null
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "ref")
this.refund_reason_id ??= this.refund_reason?.id || null
}
}

View File

@@ -22,6 +22,7 @@ import {
PaymentSessionDTO,
ProviderWebhookPayload,
RefundDTO,
RefundReasonDTO,
UpdatePaymentCollectionDTO,
UpdatePaymentDTO,
UpdatePaymentSessionDTO,
@@ -47,6 +48,7 @@ import {
PaymentCollection,
PaymentSession,
Refund,
RefundReason,
} from "@models"
import { joinerConfig } from "../joiner-config"
import PaymentProviderService from "./payment-provider"
@@ -67,6 +69,7 @@ const generateMethodForModels = {
Payment,
Capture,
Refund,
RefundReason,
}
export default class PaymentModuleService
@@ -76,6 +79,7 @@ export default class PaymentModuleService
Payment: { dto: PaymentDTO }
Capture: { dto: CaptureDTO }
Refund: { dto: RefundDTO }
RefundReason: { dto: RefundReasonDTO }
}>(generateMethodForModels)
implements IPaymentModuleService
{
@@ -784,6 +788,8 @@ export default class PaymentModuleService
payment: data.payment_id,
amount: data.amount,
created_by: data.created_by,
note: data.note,
refund_reason_id: data.refund_reason_id,
},
sharedContext
)