feat(core-flows,types): Refunds can only be performed when order is imbalanced (#8944)

* feat(core-flows,types): Refunds can only be performed when order is imbalanced

* Apply suggestions from code review

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

* chore: fix tests

---------

Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
Riqwan Thamir
2024-09-03 13:53:20 +02:00
committed by GitHub
parent 9a34e03ae1
commit 230acb700b
8 changed files with 382 additions and 581 deletions

View File

@@ -149,6 +149,7 @@ export async function createOrderSeeder({ api }) {
await api.post(`/store/carts`, {
currency_code: "usd",
email: "tony@stark-industries.com",
region_id: region.id,
shipping_address: {
address_1: "test address 1",
address_2: "test address 2",

View File

@@ -1,326 +1,308 @@
import { IPaymentModuleService } from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/utils"
import { ClaimType, ModuleRegistrationName } from "@medusajs/utils"
import { adminHeaders } from "../../../../helpers/create-admin-user"
import { seedStorefrontDefaults } from "../../../../helpers/seed-storefront-defaults"
import { setupTaxStructure } from "../../../../modules/__tests__/fixtures"
import { medusaIntegrationTestRunner } from "medusa-test-utils"
import { createAdminUser } from "../../../../helpers/create-admin-user"
import { getProductFixture } from "../../../../helpers/fixtures"
import { createOrderSeeder } from "../../fixtures/order"
jest.setTimeout(30000)
medusaIntegrationTestRunner({
testSuite: ({ dbConnection, getContainer, api }) => {
let paymentModule: IPaymentModuleService
let paymentCollection
let payment
let container
let region
let product
let cart
let order
beforeEach(async () => {
container = getContainer()
paymentModule = container.resolve(ModuleRegistrationName.PAYMENT)
await createAdminUser(dbConnection, adminHeaders, container)
region = (
const createClaim = async ({ order }) => {
const claim = (
await api.post(
"/admin/regions",
{ name: "United States", currency_code: "usd", countries: ["us"] },
adminHeaders
)
).data.region
product = (
await api.post(
"/admin/products",
getProductFixture({
title: "test",
status: "published",
variants: [
{
title: "Test variant",
manage_inventory: false,
prices: [
{
amount: 1000,
currency_code: "usd",
rules: { region_id: region.id },
},
],
},
],
}),
adminHeaders
)
).data.product
cart = (
await api.post("/store/carts", {
region_id: region.id,
items: [{ variant_id: product.variants[0].id, quantity: 1 }],
})
).data.cart
const collection = (
await api.post(
"/store/payment-collections",
"/admin/claims",
{
cart_id: cart.id,
order_id: order.id,
type: ClaimType.REPLACE,
description: "Base claim",
},
adminHeaders
)
).data.payment_collection
).data.claim
paymentCollection = (
await api.post(
`/store/payment-collections/${collection.id}/payment-sessions`,
{ provider_id: "pp_system_default" },
adminHeaders
)
).data.payment_collection
const lastSession = paymentCollection.payment_sessions[0]
// TODO: Try to replace it with user behavior, like completing a cart.
await paymentModule.authorizePaymentSession(lastSession.id, {})
const payments = (
await api.get(
`/admin/payments?payment_session_id=${lastSession.id}`,
adminHeaders
)
).data.payments
payment = payments[0]
})
it("should capture an authorized payment", async () => {
const response = await api.post(
`/admin/payments/${payment.id}/capture`,
undefined,
adminHeaders
)
expect(response.data.payment).toEqual(
expect.objectContaining({
id: payment.id,
captured_at: expect.any(String),
captures: [
expect.objectContaining({
id: expect.any(String),
amount: 1000,
}),
],
refunds: [],
amount: 1000,
})
)
expect(response.status).toEqual(200)
})
it("should refund a captured payment", async () => {
await api.post(
`/admin/payments/${payment.id}/capture`,
undefined,
`/admin/claims/${claim.id}/inbound/items`,
{ items: [{ id: order.items[0].id, quantity: 1 }] },
adminHeaders
)
const refundReason = (
await api.post(`/admin/refund-reasons`, { label: "test" }, adminHeaders)
).data.refund_reason
await api.post(`/admin/claims/${claim.id}/request`, {}, adminHeaders)
}
// BREAKING: reason is now refund_reason_id
const response = await api.post(
`/admin/payments/${payment.id}/refund`,
{
amount: 500,
refund_reason_id: refundReason.id,
note: "Do not like it",
},
beforeEach(async () => {
container = getContainer()
await createAdminUser(dbConnection, adminHeaders, container)
order = await createOrderSeeder({ api })
await api.post(
`/admin/orders/${order.id}/fulfillments`,
{ items: [{ id: order.items[0].id, quantity: 1 }] },
adminHeaders
)
})
// BREAKING: Response was `data.refund` in V1 with payment ID, reason, and amount
expect(response.status).toEqual(200)
expect(response.data.payment).toEqual(
expect.objectContaining({
id: payment.id,
captured_at: expect.any(String),
captures: [
expect.objectContaining({
id: expect.any(String),
amount: 1000,
}),
],
refunds: [
expect.objectContaining({
id: expect.any(String),
amount: 500,
note: "Do not like it",
refund_reason_id: refundReason.id,
refund_reason: expect.objectContaining({
label: "test",
describe("with outstanding amount due to claim", () => {
beforeEach(async () => {
await createClaim({ order })
})
it("should capture an authorized payment", async () => {
const payment = order.payment_collections[0].payments[0]
const response = await api.post(
`/admin/payments/${payment.id}/capture`,
undefined,
adminHeaders
)
expect(response.data.payment).toEqual(
expect.objectContaining({
id: payment.id,
captured_at: expect.any(String),
captures: [
expect.objectContaining({
id: expect.any(String),
amount: 100,
}),
}),
],
amount: 1000,
})
)
],
refunds: [],
amount: 100,
})
)
expect(response.status).toEqual(200)
})
it("should refund a captured payment", async () => {
const payment = order.payment_collections[0].payments[0]
await api.post(
`/admin/payments/${payment.id}/capture`,
undefined,
adminHeaders
)
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: 50,
refund_reason_id: refundReason.id,
note: "Do not like it",
},
adminHeaders
)
// BREAKING: Response was `data.refund` in V1 with payment ID, reason, and amount
expect(response.status).toEqual(200)
expect(response.data.payment).toEqual(
expect.objectContaining({
id: payment.id,
captured_at: expect.any(String),
captures: [
expect.objectContaining({
id: expect.any(String),
amount: 100,
}),
],
refunds: [
expect.objectContaining({
id: expect.any(String),
amount: 50,
note: "Do not like it",
refund_reason_id: refundReason.id,
refund_reason: expect.objectContaining({
label: "test",
}),
}),
],
amount: 100,
})
)
})
it("should issue multiple refunds", async () => {
const payment = order.payment_collections[0].payments[0]
await api.post(
`/admin/payments/${payment.id}/capture`,
undefined,
adminHeaders
)
const refundReason = (
await api.post(
`/admin/refund-reasons`,
{ label: "test" },
adminHeaders
)
).data.refund_reason
await api.post(
`/admin/payments/${payment.id}/refund`,
{
amount: 25,
refund_reason_id: refundReason.id,
note: "Do not like it",
},
adminHeaders
)
await api.post(
`/admin/payments/${payment.id}/refund`,
{
amount: 25,
refund_reason_id: refundReason.id,
note: "Do not like it",
},
adminHeaders
)
const refundedPayment = (
await api.get(`/admin/payments/${payment.id}`, adminHeaders)
).data.payment
expect(refundedPayment).toEqual(
expect.objectContaining({
id: payment.id,
currency_code: "usd",
amount: 100,
captured_at: expect.any(String),
captures: [
expect.objectContaining({
amount: 100,
}),
],
refunds: [
expect.objectContaining({
amount: 25,
note: "Do not like it",
}),
expect.objectContaining({
amount: 25,
note: "Do not like it",
}),
],
})
)
})
it("should throw if refund exceeds captured total", async () => {
const payment = order.payment_collections[0].payments[0]
await api.post(
`/admin/payments/${payment.id}/capture`,
undefined,
adminHeaders
)
await api.post(
`/admin/payments/${payment.id}/refund`,
{ amount: 25 },
adminHeaders
)
const e = await api
.post(
`/admin/payments/${payment.id}/refund`,
{ amount: 1000 },
adminHeaders
)
.catch((e) => e)
expect(e.response.data.message).toEqual(
"Cannot refund more than pending difference - 75"
)
})
it("should not update payment collection of other orders", async () => {
await setupTaxStructure(container.resolve(ModuleRegistrationName.TAX))
await seedStorefrontDefaults(container, "dkk")
let order1 = await createOrderSeeder({ api })
expect(order1).toEqual(
expect.objectContaining({
id: expect.any(String),
payment_status: "authorized",
})
)
const order1Payment = order1.payment_collections[0].payments[0]
await api.post(
`/admin/payments/${order1Payment.id}/capture?fields=*payment_collection`,
{ amount: order1Payment.amount },
adminHeaders
)
order1 = (await api.get(`/admin/orders/${order1.id}`, adminHeaders))
.data.order
expect(order1).toEqual(
expect.objectContaining({
id: order1.id,
payment_status: "captured",
})
)
let order2 = await createOrderSeeder({ api })
order2 = (await api.get(`/admin/orders/${order2.id}`, adminHeaders))
.data.order
expect(order2).toEqual(
expect.objectContaining({
id: expect.any(String),
payment_status: "authorized",
})
)
order1 = (await api.get(`/admin/orders/${order1.id}`, adminHeaders))
.data.order
expect(order1).toEqual(
expect.objectContaining({
id: expect.any(String),
payment_status: "captured",
})
)
})
})
it("should issue multiple refunds", async () => {
it("should throw if outstanding amount is not present", async () => {
const payment = order.payment_collections[0].payments[0]
await api.post(
`/admin/payments/${payment.id}/capture`,
undefined,
adminHeaders
)
const refundReason = (
await api.post(`/admin/refund-reasons`, { label: "test" }, adminHeaders)
).data.refund_reason
await api.post(
`/admin/payments/${payment.id}/refund`,
{
amount: 250,
refund_reason_id: refundReason.id,
note: "Do not like it",
},
adminHeaders
)
await api.post(
`/admin/payments/${payment.id}/refund`,
{
amount: 250,
refund_reason_id: refundReason.id,
note: "Do not like it",
},
adminHeaders
)
const refundedPayment = (
await api.get(`/admin/payments/${payment.id}`, adminHeaders)
).data.payment
expect(refundedPayment).toEqual(
expect.objectContaining({
id: payment.id,
currency_code: "usd",
amount: 1000,
captured_at: expect.any(String),
captures: [
expect.objectContaining({
amount: 1000,
}),
],
refunds: [
expect.objectContaining({
amount: 250,
note: "Do not like it",
}),
expect.objectContaining({
amount: 250,
note: "Do not like it",
}),
],
})
)
})
it("should throw if refund exceeds captured total", async () => {
await api.post(
`/admin/payments/${payment.id}/capture`,
undefined,
adminHeaders
)
const refundReason = (
await api.post(`/admin/refund-reasons`, { label: "test" }, adminHeaders)
).data.refund_reason
await api.post(
`/admin/payments/${payment.id}/refund`,
{
amount: 250,
refund_reason_id: refundReason.id,
note: "Do not like it",
},
adminHeaders
)
const e = await api
.post(
`/admin/payments/${payment.id}/refund`,
{
amount: 1000,
refund_reason_id: refundReason.id,
note: "Do not like it",
},
{ amount: 10 },
adminHeaders
)
.catch((e) => e)
expect(e.response.data.message).toEqual(
"You cannot refund more than what is captured on the payment."
)
})
it("should not update payment collection of other orders", async () => {
await setupTaxStructure(container.resolve(ModuleRegistrationName.TAX))
await seedStorefrontDefaults(container, "dkk")
let order1 = await createOrderSeeder({ api })
expect(order1).toEqual(
expect.objectContaining({
id: expect.any(String),
payment_status: "authorized",
})
)
const order1Payment = order1.payment_collections[0].payments[0]
const result = await api.post(
`/admin/payments/${order1Payment.id}/capture?fields=*payment_collection`,
{
amount: order1Payment.amount,
},
adminHeaders
)
order1 = (await api.get(`/admin/orders/${order1.id}`, adminHeaders)).data
.order
expect(order1).toEqual(
expect.objectContaining({
id: order1.id,
payment_status: "captured",
})
)
let order2 = await createOrderSeeder({ api })
order2 = (await api.get(`/admin/orders/${order2.id}`, adminHeaders)).data
.order
expect(order2).toEqual(
expect.objectContaining({
id: expect.any(String),
payment_status: "authorized",
})
)
order1 = (await api.get(`/admin/orders/${order1.id}`, adminHeaders)).data
.order
expect(order1).toEqual(
expect.objectContaining({
id: expect.any(String),
payment_status: "captured",
})
"Order does not have an outstanding balance to refund"
)
})
},

View File

@@ -1,3 +1,4 @@
import { ContainerRegistrationKeys, Modules } from "@medusajs/utils"
import { medusaIntegrationTestRunner } from "medusa-test-utils"
import {
adminHeaders,
@@ -10,9 +11,10 @@ medusaIntegrationTestRunner({
testSuite: ({ dbConnection, getContainer, api }) => {
let region1
let region2
let container
beforeEach(async () => {
const container = getContainer()
container = getContainer()
await createAdminUser(dbConnection, adminHeaders, container)
region1 = (
@@ -102,6 +104,37 @@ medusaIntegrationTestRunner({
)
})
})
it("should list payment providers", async () => {
const remoteLink = container.resolve(
ContainerRegistrationKeys.REMOTE_LINK
)
let response = await api.get(
`/store/regions/${region1.id}?fields=*payment_providers`
)
expect(response.status).toEqual(200)
expect(response.data.region.payment_providers).toEqual([])
await remoteLink.create([
{
[Modules.REGION]: { region_id: region1.id },
[Modules.PAYMENT]: { payment_provider_id: "pp_system_default" },
},
])
response = await api.get(
`/store/regions/${region1.id}?fields=*payment_providers`
)
expect(response.status).toEqual(200)
expect(response.data.region.payment_providers).toEqual([
expect.objectContaining({
id: "pp_system_default",
}),
])
})
})
describe("POST /admin/regions", () => {

View File

@@ -1,13 +1,13 @@
import {
ICartModuleService,
IFulfillmentModuleService,
IInventoryServiceNext,
IInventoryService,
IOrderModuleService,
IPaymentModuleService,
IPricingModuleService,
IProductModuleService,
IRegionModuleService,
IStockLocationServiceNext,
IStockLocationService,
} from "@medusajs/types"
import {
ContainerRegistrationKeys,
@@ -32,8 +32,8 @@ medusaIntegrationTestRunner({
let productModule: IProductModuleService
let paymentModule: IPaymentModuleService
let pricingModule: IPricingModuleService
let inventoryModule: IInventoryServiceNext
let stockLocationModule: IStockLocationServiceNext
let inventoryModule: IInventoryService
let stockLocationModule: IStockLocationService
let fulfillmentModule: IFulfillmentModuleService
let orderModule: IOrderModuleService
let remoteLink, remoteQuery
@@ -138,6 +138,7 @@ medusaIntegrationTestRunner({
display_id: 1,
payment_collections: [],
payment_status: "not_paid",
region_id: "test_region_id",
fulfillments: [],
fulfillment_status: "not_fulfilled",
summary: expect.objectContaining({

View File

@@ -1,287 +0,0 @@
import {
capturePaymentWorkflow,
refundPaymentWorkflow,
} from "@medusajs/core-flows"
import { IPaymentModuleService, IRegionModuleService } from "@medusajs/types"
import {
ContainerRegistrationKeys,
ModuleRegistrationName,
Modules,
} from "@medusajs/utils"
import { medusaIntegrationTestRunner } from "medusa-test-utils"
jest.setTimeout(50000)
const env = { MEDUSA_FF_MEDUSA_V2: true }
medusaIntegrationTestRunner({
env,
testSuite: ({ dbConnection, getContainer, api }) => {
describe("Payments", () => {
let appContainer
let regionService: IRegionModuleService
let paymentService: IPaymentModuleService
let remoteLink
beforeAll(async () => {
appContainer = getContainer()
regionService = appContainer.resolve(ModuleRegistrationName.REGION)
paymentService = appContainer.resolve(ModuleRegistrationName.PAYMENT)
remoteLink = appContainer.resolve(ContainerRegistrationKeys.REMOTE_LINK)
})
// TODO: Test should move to `integration-tests/api`
it("should list payment providers", async () => {
const region = await regionService.createRegions({
name: "Test Region",
currency_code: "usd",
})
let response = await api.get(
`/store/regions/${region.id}?fields=*payment_providers`
)
expect(response.status).toEqual(200)
expect(response.data.region.payment_providers).toEqual([])
await remoteLink.create([
{
[Modules.REGION]: {
region_id: region.id,
},
[Modules.PAYMENT]: {
payment_provider_id: "pp_system_default",
},
},
])
response = await api.get(
`/store/regions/${region.id}?fields=*payment_providers`
)
expect(response.status).toEqual(200)
expect(response.data.region.payment_providers).toEqual([
expect.objectContaining({
id: "pp_system_default",
}),
])
})
it("should capture a payment", async () => {
const paymentCollection = await paymentService.createPaymentCollections(
{
region_id: "test-region",
amount: 1000,
currency_code: "usd",
}
)
const paymentSession = await paymentService.createPaymentSession(
paymentCollection.id,
{
provider_id: "pp_system_default",
amount: 1000,
currency_code: "usd",
data: {},
}
)
const payment = await paymentService.authorizePaymentSession(
paymentSession.id,
{}
)
await capturePaymentWorkflow(appContainer).run({
input: {
payment_id: payment.id,
},
throwOnError: false,
})
const [paymentResult] = await paymentService.listPayments({
id: payment.id,
})
expect(paymentResult).toEqual(
expect.objectContaining({
id: payment.id,
amount: 1000,
payment_collection_id: paymentCollection.id,
})
)
const [capture] = await paymentService.listCaptures({
payment_id: payment.id,
})
expect(capture).toEqual(
expect.objectContaining({
id: expect.any(String),
payment: expect.objectContaining({ id: payment.id }),
amount: 1000,
})
)
})
it("should partially capture a payment", async () => {
const paymentCollection = await paymentService.createPaymentCollections(
{
region_id: "test-region",
amount: 1000,
currency_code: "usd",
}
)
const paymentSession = await paymentService.createPaymentSession(
paymentCollection.id,
{
provider_id: "pp_system_default",
amount: 1000,
currency_code: "usd",
data: {},
}
)
const payment = await paymentService.authorizePaymentSession(
paymentSession.id,
{}
)
await capturePaymentWorkflow(appContainer).run({
input: {
payment_id: payment.id,
amount: 500,
},
throwOnError: false,
})
const [paymentResult] = await paymentService.listPayments({
id: payment.id,
})
expect(paymentResult).toEqual(
expect.objectContaining({
id: payment.id,
amount: 1000,
payment_collection_id: paymentCollection.id,
})
)
const [capture] = await paymentService.listCaptures({
payment_id: payment.id,
})
expect(capture).toEqual(
expect.objectContaining({
id: expect.any(String),
payment: expect.objectContaining({ id: payment.id }),
amount: 500,
})
)
})
it("should refund a payment", async () => {
const paymentCollection = await paymentService.createPaymentCollections(
{
region_id: "test-region",
amount: 1000,
currency_code: "usd",
}
)
const paymentSession = await paymentService.createPaymentSession(
paymentCollection.id,
{
provider_id: "pp_system_default",
amount: 1000,
currency_code: "usd",
data: {},
}
)
const payment = await paymentService.authorizePaymentSession(
paymentSession.id,
{}
)
await capturePaymentWorkflow(appContainer).run({
input: {
payment_id: payment.id,
},
throwOnError: false,
})
await refundPaymentWorkflow(appContainer).run({
input: {
payment_id: payment.id,
},
throwOnError: false,
})
const [refund] = await paymentService.listRefunds({
payment_id: payment.id,
})
expect(refund).toEqual(
expect.objectContaining({
id: expect.any(String),
payment: expect.objectContaining({ id: payment.id }),
amount: 1000,
})
)
})
it("should partially refund a payment", async () => {
const paymentCollection = await paymentService.createPaymentCollections(
{
region_id: "test-region",
amount: 1000,
currency_code: "usd",
}
)
const paymentSession = await paymentService.createPaymentSession(
paymentCollection.id,
{
provider_id: "pp_system_default",
amount: 1000,
currency_code: "usd",
data: {},
}
)
const payment = await paymentService.authorizePaymentSession(
paymentSession.id,
{}
)
await capturePaymentWorkflow(appContainer).run({
input: {
payment_id: payment.id,
},
throwOnError: false,
})
await refundPaymentWorkflow(appContainer).run({
input: {
payment_id: payment.id,
amount: 500,
},
throwOnError: false,
})
const [refund] = await paymentService.listRefunds({
payment_id: payment.id,
})
expect(refund).toEqual(
expect.objectContaining({
id: expect.any(String),
payment: expect.objectContaining({ id: payment.id }),
amount: 500,
})
)
})
})
},
})

View File

@@ -1,8 +1,9 @@
import { BigNumberInput } from "@medusajs/types"
import { MathBN, PaymentEvents } from "@medusajs/utils"
import { BigNumberInput, OrderDTO, PaymentDTO } from "@medusajs/types"
import { MathBN, MedusaError, PaymentEvents } from "@medusajs/utils"
import {
WorkflowData,
WorkflowResponse,
createStep,
createWorkflow,
transform,
when,
@@ -11,6 +12,41 @@ import { emitEventStep, useRemoteQueryStep } from "../../common"
import { addOrderTransactionStep } from "../../order/steps/add-order-transaction"
import { refundPaymentStep } from "../steps/refund-payment"
/**
* This step validates that the refund is valid for the order
*/
export const validateRefundStep = createStep(
"validate-refund-step",
async function ({
order,
payment,
amount,
}: {
order: OrderDTO
payment: PaymentDTO
amount?: BigNumberInput
}) {
const pendingDifference = order.summary?.raw_pending_difference!
if (MathBN.gte(pendingDifference, 0)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Order does not have an outstanding balance to refund`
)
}
const amountPending = MathBN.mult(pendingDifference, -1)
const amountToRefund = amount ?? payment.raw_amount ?? payment.amount
if (MathBN.gt(amountToRefund, amountPending)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Cannot refund more than pending difference - ${amountPending}`
)
}
}
)
export const refundPaymentWorkflowId = "refund-payment-workflow"
/**
* This workflow refunds a payment.
@@ -24,28 +60,52 @@ export const refundPaymentWorkflow = createWorkflow(
amount?: BigNumberInput
}>
) => {
const payment = refundPaymentStep(input)
const payment = useRemoteQueryStep({
entry_point: "payment",
fields: [
"id",
"payment_collection_id",
"currency_code",
"amount",
"raw_amount",
],
variables: { id: input.payment_id },
list: false,
throw_if_key_not_found: true,
})
const orderPayment = useRemoteQueryStep({
const orderPaymentCollection = useRemoteQueryStep({
entry_point: "order_payment_collection",
fields: ["order.id"],
variables: { payment_collection_id: payment.payment_collection_id },
list: false,
})
throw_if_key_not_found: true,
}).config({ name: "order-payment-collection" })
when({ orderPayment }, ({ orderPayment }) => {
return !!orderPayment?.order?.id
const order = useRemoteQueryStep({
entry_point: "order",
fields: ["id", "summary", "currency_code", "region_id"],
variables: { id: orderPaymentCollection.order.id },
throw_if_key_not_found: true,
list: false,
}).config({ name: "order" })
validateRefundStep({ order, payment, amount: input.amount })
refundPaymentStep(input)
when({ orderPaymentCollection }, ({ orderPaymentCollection }) => {
return !!orderPaymentCollection?.order?.id
}).then(() => {
const orderTransactionData = transform(
{ input, payment, orderPayment },
({ input, payment, orderPayment }) => {
{ input, payment, orderPaymentCollection },
({ input, payment, orderPaymentCollection }) => {
return {
order_id: orderPayment.order.id,
order_id: orderPaymentCollection.order.id,
amount: MathBN.mult(
input.amount ?? payment.raw_amount ?? payment.amount,
-1
),
currency_code: payment.currency_code,
currency_code: payment.currency_code ?? order.currency_code,
reference_id: payment.id,
reference: "refund",
}

View File

@@ -104,6 +104,16 @@ export type OrderSummaryDTO = {
* The refunded total of the order summary.
*/
refunded_total: BigNumberValue
/**
* The pending difference of the order.
*/
pending_difference: BigNumberValue
/**
* The raw pending difference of the order.
*/
raw_pending_difference: BigNumberRawValue
}
/**

View File

@@ -12,6 +12,7 @@ export const defaultAdminOrderFields = [
export const defaultAdminRetrieveOrderFields = [
"id",
"display_id",
"region_id",
"status",
"version",
"summary",