feat(payment): update payment collection status (#7335)
This commit is contained in:
committed by
GitHub
parent
774696845c
commit
7c4f4d7388
@@ -18,6 +18,16 @@ export class Migration20240225134525 extends Migration {
|
||||
ALTER TABLE IF EXISTS "payment_collection" ADD COLUMN IF NOT EXISTS "completed_at" TIMESTAMPTZ NULL;
|
||||
ALTER TABLE IF EXISTS "payment_collection" ADD COLUMN IF NOT EXISTS "raw_amount" JSONB NOT NULL;
|
||||
ALTER TABLE IF EXISTS "payment_collection" ADD COLUMN IF NOT EXISTS "deleted_at" TIMESTAMPTZ NULL;
|
||||
|
||||
ALTER TABLE IF EXISTS "payment_collection" ADD COLUMN IF NOT EXISTS "authorized_amount" NUMERIC NULL;
|
||||
ALTER TABLE IF EXISTS "payment_collection" ADD COLUMN IF NOT EXISTS "raw_authorized_amount" JSONB NULL;
|
||||
|
||||
ALTER TABLE IF EXISTS "payment_collection" ADD COLUMN IF NOT EXISTS "captured_amount" NUMERIC NULL;
|
||||
ALTER TABLE IF EXISTS "payment_collection" ADD COLUMN IF NOT EXISTS "raw_captured_amount" JSONB NULL;
|
||||
|
||||
ALTER TABLE IF EXISTS "payment_collection" ADD COLUMN IF NOT EXISTS "refunded_amount" NUMERIC NULL;
|
||||
ALTER TABLE IF EXISTS "payment_collection" ADD COLUMN IF NOT EXISTS "raw_refunded_amount" JSONB NULL;
|
||||
|
||||
ALTER TABLE "payment_collection" DROP CONSTRAINT "FK_payment_collection_region_id";
|
||||
|
||||
ALTER TABLE IF EXISTS "payment_provider" ADD COLUMN IF NOT EXISTS "is_enabled" BOOLEAN NOT NULL DEFAULT TRUE;
|
||||
@@ -119,6 +129,12 @@ export class Migration20240225134525 extends Migration {
|
||||
"currency_code" TEXT NOT NULL,
|
||||
"amount" NUMERIC NOT NULL,
|
||||
"raw_amount" JSONB NOT NULL,
|
||||
"authorized_amount" NUMERIC NULL,
|
||||
"raw_authorized_amount" JSONB NULL,
|
||||
"captured_amount" NUMERIC NULL,
|
||||
"raw_captured_amount" JSONB NULL,
|
||||
"refunded_amount" NUMERIC NULL,
|
||||
"raw_refunded_amount" JSONB NULL,
|
||||
"region_id" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
|
||||
@@ -43,6 +43,24 @@ export default class PaymentCollection {
|
||||
@Property({ columnType: "jsonb" })
|
||||
raw_amount: BigNumberRawValue
|
||||
|
||||
@MikroOrmBigNumberProperty({ nullable: true })
|
||||
authorized_amount: BigNumber | number | null = null
|
||||
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
raw_authorized_amount: BigNumberRawValue | null = null
|
||||
|
||||
@MikroOrmBigNumberProperty({ nullable: true })
|
||||
captured_amount: BigNumber | number | null = null
|
||||
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
raw_captured_amount: BigNumberRawValue | null = null
|
||||
|
||||
@MikroOrmBigNumberProperty({ nullable: true })
|
||||
refunded_amount: BigNumber | number | null = null
|
||||
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
raw_refunded_amount: BigNumberRawValue | null = null
|
||||
|
||||
@Property({ columnType: "text", index: "IDX_payment_collection_region_id" })
|
||||
region_id: string
|
||||
|
||||
|
||||
@@ -131,13 +131,6 @@ export default class Payment {
|
||||
})
|
||||
payment_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")
|
||||
|
||||
@@ -36,8 +36,10 @@ import {
|
||||
MedusaError,
|
||||
ModulesSdkUtils,
|
||||
PaymentActions,
|
||||
PaymentCollectionStatus,
|
||||
promiseAll,
|
||||
} from "@medusajs/utils"
|
||||
import { IsolationLevel } from "@mikro-orm/core"
|
||||
import {
|
||||
Capture,
|
||||
Payment,
|
||||
@@ -404,6 +406,9 @@ export default class PaymentModuleService<
|
||||
context: Record<string, unknown>,
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<PaymentDTO> {
|
||||
sharedContext ??= {}
|
||||
sharedContext.isolationLevel = IsolationLevel.SERIALIZABLE
|
||||
|
||||
const session = await this.paymentSessionService_.retrieve(
|
||||
id,
|
||||
{
|
||||
@@ -456,7 +461,10 @@ export default class PaymentModuleService<
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: update status on payment collection if authorized_amount === amount - depends on the BigNumber PR
|
||||
await this.maybeUpdatePaymentCollection_(
|
||||
session.payment_collection_id,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const payment = await this.paymentService_.create(
|
||||
{
|
||||
@@ -491,11 +499,30 @@ export default class PaymentModuleService<
|
||||
})
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
@InjectManager("baseRepository_")
|
||||
async capturePayment(
|
||||
data: CreateCaptureDTO,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<PaymentDTO> {
|
||||
const payment = (await this.capturePayment_(data, sharedContext)) as Payment
|
||||
|
||||
await this.maybeUpdatePaymentCollection_(
|
||||
payment.payment_collection_id,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return await this.retrievePayment(
|
||||
payment.id,
|
||||
{ relations: ["captures"] },
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
private async capturePayment_(
|
||||
data: CreateCaptureDTO,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
) {
|
||||
const payment = await this.paymentService_.retrieve(
|
||||
data.payment_id,
|
||||
{
|
||||
@@ -503,6 +530,7 @@ export default class PaymentModuleService<
|
||||
"id",
|
||||
"data",
|
||||
"provider_id",
|
||||
"payment_collection_id",
|
||||
"amount",
|
||||
"raw_amount",
|
||||
"canceled_at",
|
||||
@@ -561,38 +589,58 @@ export default class PaymentModuleService<
|
||||
sharedContext
|
||||
)
|
||||
|
||||
await this.paymentService_.update(
|
||||
{ id: payment.id, data: paymentData },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
// When the entire authorized amount has been captured, we mark it fully capture by setting the captured_at field
|
||||
let capturedAt: Date | null = null
|
||||
const totalCaptured = MathBN.convert(
|
||||
MathBN.add(capturedAmount, newCaptureAmount)
|
||||
)
|
||||
if (MathBN.gte(totalCaptured, authorizedAmount)) {
|
||||
await this.paymentService_.update(
|
||||
{ id: payment.id, captured_at: new Date() },
|
||||
sharedContext
|
||||
)
|
||||
capturedAt = new Date()
|
||||
}
|
||||
|
||||
await this.paymentService_.update(
|
||||
{ id: payment.id, data: paymentData, captured_at: capturedAt },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return payment
|
||||
}
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async refundPayment(
|
||||
data: CreateRefundDTO,
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<PaymentDTO> {
|
||||
const payment = await this.refundPayment_(data, sharedContext)
|
||||
|
||||
await this.maybeUpdatePaymentCollection_(
|
||||
payment.payment_collection_id,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return await this.retrievePayment(
|
||||
payment.id,
|
||||
{ relations: ["captures"] },
|
||||
{ relations: ["refunds"] },
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async refundPayment(
|
||||
private async refundPayment_(
|
||||
data: CreateRefundDTO,
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<PaymentDTO> {
|
||||
) {
|
||||
const payment = await this.paymentService_.retrieve(
|
||||
data.payment_id,
|
||||
{
|
||||
select: ["id", "data", "provider_id", "amount", "raw_amount"],
|
||||
select: [
|
||||
"id",
|
||||
"data",
|
||||
"provider_id",
|
||||
"payment_collection_id",
|
||||
"amount",
|
||||
"raw_amount",
|
||||
],
|
||||
relations: ["captures.raw_amount"],
|
||||
},
|
||||
sharedContext
|
||||
@@ -637,11 +685,7 @@ export default class PaymentModuleService<
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return await this.retrievePayment(
|
||||
payment.id,
|
||||
{ relations: ["refunds"] },
|
||||
sharedContext
|
||||
)
|
||||
return payment
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
@@ -756,4 +800,74 @@ export default class PaymentModuleService<
|
||||
count,
|
||||
]
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
private async maybeUpdatePaymentCollection_(
|
||||
paymentCollectionId: string,
|
||||
sharedContext?: Context
|
||||
) {
|
||||
const paymentCollection = await this.paymentCollectionService_.retrieve(
|
||||
paymentCollectionId,
|
||||
{
|
||||
select: ["amount", "raw_amount", "status"],
|
||||
relations: [
|
||||
"payment_sessions.amount",
|
||||
"payment_sessions.raw_amount",
|
||||
"payments.captures.amount",
|
||||
"payments.captures.raw_amount",
|
||||
"payments.refunds.amount",
|
||||
"payments.refunds.raw_amount",
|
||||
],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const paymentSessions = paymentCollection.payment_sessions
|
||||
const captures = paymentCollection.payments
|
||||
.map((pay) => [...pay.captures])
|
||||
.flat()
|
||||
const refunds = paymentCollection.payments
|
||||
.map((pay) => [...pay.refunds])
|
||||
.flat()
|
||||
|
||||
let authorizedAmount = MathBN.convert(0)
|
||||
let capturedAmount = MathBN.convert(0)
|
||||
let refundedAmount = MathBN.convert(0)
|
||||
|
||||
for (const ps of paymentSessions) {
|
||||
if (ps.status === PaymentSessionStatus.AUTHORIZED) {
|
||||
authorizedAmount = MathBN.add(authorizedAmount, ps.amount)
|
||||
}
|
||||
}
|
||||
|
||||
for (const capture of captures) {
|
||||
capturedAmount = MathBN.add(capturedAmount, capture.amount)
|
||||
}
|
||||
|
||||
for (const refund of refunds) {
|
||||
refundedAmount = MathBN.add(refundedAmount, refund.amount)
|
||||
}
|
||||
|
||||
let status =
|
||||
paymentSessions.length === 0
|
||||
? PaymentCollectionStatus.NOT_PAID
|
||||
: PaymentCollectionStatus.AWAITING
|
||||
|
||||
if (MathBN.gt(authorizedAmount, 0)) {
|
||||
status = MathBN.gte(authorizedAmount, paymentCollection.amount)
|
||||
? PaymentCollectionStatus.AUTHORIZED
|
||||
: PaymentCollectionStatus.PARTIALLY_AUTHORIZED
|
||||
}
|
||||
|
||||
await this.paymentCollectionService_.update(
|
||||
{
|
||||
id: paymentCollectionId,
|
||||
status,
|
||||
authorized_amount: authorizedAmount,
|
||||
captured_amount: capturedAmount,
|
||||
refunded_amount: refundedAmount,
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user