diff --git a/packages/payment/src/models/capture.ts b/packages/payment/src/models/capture.ts new file mode 100644 index 0000000000..5c8d1eb53a --- /dev/null +++ b/packages/payment/src/models/capture.ts @@ -0,0 +1,55 @@ +import { + BeforeCreate, + Entity, + ManyToOne, + OnInit, + OptionalProps, + PrimaryKey, + Property, +} from "@mikro-orm/core" + +import { generateEntityId } from "@medusajs/utils" +import Payment from "./payment" + +type OptionalCaptureProps = "created_at" + +@Entity({ tableName: "capture" }) +export default class Capture { + [OptionalProps]?: OptionalCaptureProps + + @PrimaryKey({ columnType: "text" }) + id: string + + @Property({ + columnType: "numeric", + serializer: Number, + }) + amount: number + + @ManyToOne(() => Payment, { + onDelete: "cascade", + index: "IDX_capture_payment_id", + fieldName: "payment_id", + }) + payment: Payment + + @Property({ + onCreate: () => new Date(), + columnType: "timestamptz", + defaultRaw: "now()", + }) + created_at: Date + + @Property({ columnType: "text", nullable: true }) + created_by: string | null = null + + @BeforeCreate() + onCreate() { + this.id = generateEntityId(this.id, "capt") + } + + @OnInit() + onInit() { + this.id = generateEntityId(this.id, "capt") + } +} diff --git a/packages/payment/src/models/index.ts b/packages/payment/src/models/index.ts index 6810edbc6f..6588a97b76 100644 --- a/packages/payment/src/models/index.ts +++ b/packages/payment/src/models/index.ts @@ -1 +1,7 @@ +export { default as Capture } from "./capture" export { default as Payment } from "./payment" +export { default as PaymentCollection } from "./payment-collection" +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" diff --git a/packages/payment/src/models/payment-collection.ts b/packages/payment/src/models/payment-collection.ts new file mode 100644 index 0000000000..3cc690fea1 --- /dev/null +++ b/packages/payment/src/models/payment-collection.ts @@ -0,0 +1,119 @@ +import { + BeforeCreate, + Cascade, + Collection, + Entity, + Enum, + Filter, + ManyToMany, + OneToMany, + OnInit, + OptionalProps, + PrimaryKey, + Property, +} from "@mikro-orm/core" +import { DAL } from "@medusajs/types" + +import { + DALUtils, + generateEntityId, + optionalNumericSerializer, + PaymentCollectionStatus, +} from "@medusajs/utils" +import PaymentProvider from "./payment-provider" +import PaymentSession from "./payment-session" +import Payment from "./payment" + +type OptionalPaymentCollectionProps = "status" | DAL.EntityDateColumns + +@Entity({ tableName: "payment_collection" }) +@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions) +export default class PaymentCollection { + [OptionalProps]?: OptionalPaymentCollectionProps + + @PrimaryKey({ columnType: "text" }) + id: string + + @Property({ columnType: "text" }) + currency_code: string + + @Property({ + columnType: "numeric", + serializer: Number, + }) + amount: number + + @Property({ + columnType: "numeric", + nullable: true, + serializer: optionalNumericSerializer, + }) + authorized_amount: number | null = null + + @Property({ + columnType: "numeric", + nullable: true, + serializer: optionalNumericSerializer, + }) + refunded_amount: number | null = null + + @Property({ columnType: "text", index: "IDX_payment_collection_region_id" }) + region_id: string + + @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, + index: "IDX_payment_collection_deleted_at", + }) + deleted_at: Date | null = null + + @Property({ + columnType: "timestamptz", + nullable: true, + }) + completed_at: Date | null = null + + @Enum({ + items: () => PaymentCollectionStatus, + default: PaymentCollectionStatus.NOT_PAID, + }) + status: PaymentCollectionStatus = PaymentCollectionStatus.NOT_PAID + + @ManyToMany(() => PaymentProvider) + payment_providers = new Collection(this) + + @OneToMany(() => PaymentSession, (ps) => ps.payment_collection, { + cascade: [Cascade.REMOVE], + }) + payment_sessions = new Collection(this) + + @OneToMany(() => Payment, (payment) => payment.payment_collection, { + cascade: [Cascade.REMOVE], + }) + payments = new Collection(this) + + @BeforeCreate() + onCreate() { + this.id = generateEntityId(this.id, "pay_col") + } + + @OnInit() + onInit() { + this.id = generateEntityId(this.id, "pay_col") + } +} diff --git a/packages/payment/src/models/payment-method-token.ts b/packages/payment/src/models/payment-method-token.ts new file mode 100644 index 0000000000..e7af8af115 --- /dev/null +++ b/packages/payment/src/models/payment-method-token.ts @@ -0,0 +1,43 @@ +import { + BeforeCreate, + Entity, + OnInit, + PrimaryKey, + Property, +} from "@mikro-orm/core" + +import { generateEntityId } from "@medusajs/utils" + +@Entity({ tableName: "payment_method_token" }) +export default class PaymentMethodToken { + @PrimaryKey({ columnType: "text" }) + id: string + + @Property({ columnType: "text" }) + provider_id: string + + @Property({ columnType: "jsonb", nullable: true }) + data: Record | null = null + + @Property({ columnType: "text" }) + name: string + + @Property({ columnType: "text", nullable: true }) + type_detail: string | null = null + + @Property({ columnType: "text", nullable: true }) + description_detail: string | null = null + + @Property({ columnType: "jsonb", nullable: true }) + metadata: Record | null = null + + @BeforeCreate() + onCreate() { + this.id = generateEntityId(this.id, "paymttok") + } + + @OnInit() + onInit() { + this.id = generateEntityId(this.id, "paymttok") + } +} diff --git a/packages/payment/src/models/payment-provider.ts b/packages/payment/src/models/payment-provider.ts new file mode 100644 index 0000000000..42fe25d256 --- /dev/null +++ b/packages/payment/src/models/payment-provider.ts @@ -0,0 +1,15 @@ +import { Entity, OptionalProps, PrimaryKey, Property } from "@mikro-orm/core" + +@Entity({ tableName: "payment_provider" }) +export default class PaymentProvider { + [OptionalProps]?: "is_enabled" + + @PrimaryKey({ columnType: "text" }) + id: string + + @Property({ + default: true, + columnType: "boolean", + }) + is_enabled: boolean = true +} diff --git a/packages/payment/src/models/payment-session.ts b/packages/payment/src/models/payment-session.ts new file mode 100644 index 0000000000..f001852af5 --- /dev/null +++ b/packages/payment/src/models/payment-session.ts @@ -0,0 +1,69 @@ +import { + BeforeCreate, + Entity, + Enum, + ManyToOne, + OneToOne, + OnInit, + PrimaryKey, + Property, +} from "@mikro-orm/core" +import { generateEntityId, PaymentSessionStatus } from "@medusajs/utils" + +import PaymentCollection from "./payment-collection" +import Payment from "./payment" + +@Entity({ tableName: "payment_session" }) +export default class PaymentSession { + @PrimaryKey({ columnType: "text" }) + id: string + + @Property({ columnType: "text" }) + currency_code: string + + @Property({ + columnType: "numeric", + serializer: Number, + }) + amount: number + + @Property({ columnType: "text" }) + provider_id: string + + @Property({ columnType: "jsonb", nullable: true }) + data: Record | null = null + + @Enum({ + items: () => PaymentSessionStatus, + }) + status: PaymentSessionStatus + + @Property({ + columnType: "timestamptz", + nullable: true, + }) + authorized_at: Date | null = null + + @ManyToOne({ + index: "IDX_payment_session_payment_collection_id", + fieldName: "payment_collection_id", + }) + payment_collection!: PaymentCollection + + @OneToOne({ + entity: () => Payment, + mappedBy: (payment) => payment.session, + cascade: ["soft-remove"] as any, + }) + payment!: Payment + + @BeforeCreate() + onCreate() { + this.id = generateEntityId(this.id, "payses") + } + + @OnInit() + onInit() { + this.id = generateEntityId(this.id, "payses") + } +} diff --git a/packages/payment/src/models/payment.ts b/packages/payment/src/models/payment.ts index f4fee2bd64..4647be64ac 100644 --- a/packages/payment/src/models/payment.ts +++ b/packages/payment/src/models/payment.ts @@ -1,18 +1,73 @@ import { BeforeCreate, + Cascade, + Collection, Entity, + Filter, + ManyToOne, + OneToMany, + OneToOne, OnInit, + OptionalProps, PrimaryKey, Property, } from "@mikro-orm/core" +import { DAL } from "@medusajs/types" -import { generateEntityId } from "@medusajs/utils" +import { + DALUtils, + generateEntityId, + optionalNumericSerializer, +} from "@medusajs/utils" +import Refund from "./refund" +import Capture from "./capture" +import PaymentSession from "./payment-session" +import PaymentCollection from "./payment-collection" + +type OptionalPaymentProps = DAL.EntityDateColumns @Entity({ tableName: "payment" }) +@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions) export default class Payment { + [OptionalProps]?: OptionalPaymentProps + @PrimaryKey({ columnType: "text" }) id: string + @Property({ + columnType: "numeric", + serializer: Number, + }) + amount: number + + @Property({ + columnType: "numeric", + nullable: true, + serializer: optionalNumericSerializer, + }) + authorized_amount: number | null = null + + @Property({ columnType: "text" }) + currency_code: string + + @Property({ columnType: "text" }) + provider_id: string + + @Property({ columnType: "text", nullable: true }) + cart_id: string | null = null + + @Property({ columnType: "text", nullable: true }) + order_id: string | null = null + + @Property({ columnType: "text", nullable: true }) + order_edit_id: string | null = null + + @Property({ columnType: "text", nullable: true }) + customer_id: string | null = null + + @Property({ columnType: "jsonb", nullable: true }) + data: Record | null = null + @Property({ onCreate: () => new Date(), columnType: "timestamptz", @@ -28,6 +83,51 @@ export default class Payment { }) updated_at: Date + @Property({ + columnType: "timestamptz", + nullable: true, + index: "IDX_payment_deleted_at", + }) + deleted_at: Date | null = null + + @Property({ + columnType: "timestamptz", + nullable: true, + }) + captured_at: Date | null = null + + @Property({ + columnType: "timestamptz", + nullable: true, + }) + canceled_at: Date | null = null + + @OneToMany(() => Refund, (refund) => refund.payment, { + cascade: [Cascade.REMOVE], + }) + refunds = new Collection(this) + + @OneToMany(() => Capture, (capture) => capture.payment, { + cascade: [Cascade.REMOVE], + }) + captures = new Collection(this) + + @ManyToOne({ + index: "IDX_payment_payment_collection_id", + fieldName: "payment_collection_id", + }) + payment_collection: PaymentCollection + + @OneToOne({ owner: true, fieldName: "session_id" }) + session: PaymentSession + + /** COMPUTED PROPERTIES START **/ + + // captured_amount: number // sum of the associated captures + // refunded_amount: number // sum of the associated refunds + + /** COMPUTED PROPERTIES END **/ + @BeforeCreate() onCreate() { this.id = generateEntityId(this.id, "pay") diff --git a/packages/payment/src/models/refund.ts b/packages/payment/src/models/refund.ts new file mode 100644 index 0000000000..4f99da250f --- /dev/null +++ b/packages/payment/src/models/refund.ts @@ -0,0 +1,50 @@ +import { + BeforeCreate, + Entity, + ManyToOne, + OnInit, + PrimaryKey, + Property, +} from "@mikro-orm/core" + +import { generateEntityId } from "@medusajs/utils" +import Payment from "./payment" + +@Entity({ tableName: "refund" }) +export default class Refund { + @PrimaryKey({ columnType: "text" }) + id: string + + @Property({ + columnType: "numeric", + serializer: Number, + }) + amount: number + + @ManyToOne(() => Payment, { + onDelete: "cascade", + index: "IDX_refund_payment_id", + fieldName: "payment_id", + }) + payment: Payment + + @Property({ + onCreate: () => new Date(), + columnType: "timestamptz", + defaultRaw: "now()", + }) + created_at: Date + + @Property({ columnType: "text", nullable: true }) + created_by: string | null = null + + @BeforeCreate() + onCreate() { + this.id = generateEntityId(this.id, "ref") + } + + @OnInit() + onInit() { + this.id = generateEntityId(this.id, "ref") + } +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 7674050a26..5f96dcb3dd 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -5,6 +5,7 @@ export * from "./decorators" export * from "./event-bus" export * from "./feature-flags" export * from "./modules-sdk" +export * from "./payment" export * from "./pricing" export * from "./product" export * from "./promotion" diff --git a/packages/utils/src/payment/index.ts b/packages/utils/src/payment/index.ts new file mode 100644 index 0000000000..e5e483a8d5 --- /dev/null +++ b/packages/utils/src/payment/index.ts @@ -0,0 +1,2 @@ +export * from "./payment-collection" +export * from "./payment-session" diff --git a/packages/utils/src/payment/payment-collection.ts b/packages/utils/src/payment/payment-collection.ts new file mode 100644 index 0000000000..600445705f --- /dev/null +++ b/packages/utils/src/payment/payment-collection.ts @@ -0,0 +1,27 @@ +/** + * @enum + * + * The payment collection's status. + */ +export enum PaymentCollectionStatus { + /** + * The payment collection isn't paid. + */ + NOT_PAID = "not_paid", + /** + * The payment collection is awaiting payment. + */ + AWAITING = "awaiting", + /** + * The payment collection is authorized. + */ + AUTHORIZED = "authorized", + /** + * Some of the payments in the payment collection are authorized. + */ + PARTIALLY_AUTHORIZED = "partially_authorized", + /** + * The payment collection is canceled. + */ + CANCELED = "canceled", +} diff --git a/packages/utils/src/payment/payment-session.ts b/packages/utils/src/payment/payment-session.ts new file mode 100644 index 0000000000..d65005fc92 --- /dev/null +++ b/packages/utils/src/payment/payment-session.ts @@ -0,0 +1,27 @@ +/** + * @enum + * + * The status of a payment session. + */ +export enum PaymentSessionStatus { + /** + * The payment is authorized. + */ + AUTHORIZED = "authorized", + /** + * The payment is pending. + */ + PENDING = "pending", + /** + * The payment requires an action. + */ + REQUIRES_MORE = "requires_more", + /** + * An error occurred while processing the payment. + */ + ERROR = "error", + /** + * The payment is canceled. + */ + CANCELED = "canceled", +}