feat(payment): provider service (#6308)

This commit is contained in:
Frane Polić
2024-02-12 20:57:41 +01:00
committed by GitHub
parent 869dc751a0
commit a6a4b3f01a
21 changed files with 1511 additions and 637 deletions
@@ -24,21 +24,21 @@ export const defaultPaymentSessionData = [
id: "pay-sess-id-1",
amount: 100,
currency_code: "usd",
provider_id: "manual",
provider_id: "system",
payment_collection: "pay-col-id-1",
},
{
id: "pay-sess-id-2",
amount: 100,
currency_code: "usd",
provider_id: "manual",
provider_id: "system",
payment_collection: "pay-col-id-2",
},
{
id: "pay-sess-id-3",
amount: 100,
currency_code: "usd",
provider_id: "manual",
provider_id: "system",
payment_collection: "pay-col-id-2",
},
]
@@ -50,7 +50,7 @@ export const defaultPaymentData = [
currency_code: "usd",
payment_collection: "pay-col-id-1",
payment_session: "pay-sess-id-1",
provider_id: "manual",
provider_id: "system",
authorized_amount: 100,
data: {},
},
@@ -61,7 +61,7 @@ export const defaultPaymentData = [
currency_code: "usd",
payment_collection: "pay-col-id-2",
payment_session: "pay-sess-id-2",
provider_id: "manual",
provider_id: "system",
data: {},
},
]
@@ -1,50 +1,35 @@
import { IPaymentModuleService } from "@medusajs/types"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { initModules } from "medusa-test-utils"
import { Modules } from "@medusajs/modules-sdk"
import { initialize } from "../../../../src"
import { DB_URL, MikroOrmWrapper } from "../../../utils"
import { MikroOrmWrapper } from "../../../utils"
import {
createPaymentCollections,
createPaymentSessions,
createPayments,
} from "../../../__fixtures__"
import { getInitModuleConfig } from "../../../utils/get-init-module-config"
import { initModules } from "medusa-test-utils"
import { Modules } from "@medusajs/modules-sdk"
jest.setTimeout(30000)
describe("Payment Module Service", () => {
let service: IPaymentModuleService
describe("PaymentCollection", () => {
let repositoryManager: SqlEntityManager
describe("Payment Flow", () => {
let service: IPaymentModuleService
let shutdownFunc: () => Promise<void>
beforeAll(async () => {
const initModulesConfig = getInitModuleConfig()
const { medusaApp, shutdown } = await initModules(initModulesConfig)
service = medusaApp.modules[Modules.PAYMENT]
shutdownFunc = shutdown
})
afterAll(async () => {
await shutdownFunc()
})
beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
repositoryManager = await MikroOrmWrapper.forkManager()
const repositoryManager = await MikroOrmWrapper.forkManager()
service = await initialize({
database: {
clientUrl: DB_URL,
schema: process.env.MEDUSA_PAYMNET_DB_SCHEMA,
},
})
const initModulesConfig = getInitModuleConfig()
const { medusaApp, shutdown } = await initModules(initModulesConfig)
service = medusaApp.modules[Modules.PAYMENT]
shutdownFunc = shutdown
await createPaymentCollections(repositoryManager)
await createPaymentSessions(repositoryManager)
@@ -53,12 +38,120 @@ describe("Payment Module Service", () => {
afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
await shutdownFunc()
})
it("complete payment flow successfully", async () => {
let paymentCollection = await service.createPaymentCollections({
currency_code: "USD",
amount: 200,
region_id: "reg_123",
})
const paymentSession = await service.createPaymentSession(
paymentCollection.id,
{
provider_id: "system",
providerContext: {
amount: 200,
currency_code: "USD",
payment_session_data: {},
context: {},
customer: {},
billing_address: {},
email: "test@test.test.com",
resource_id: "cart_test",
},
}
)
const payment = await service.authorizePaymentSession(
paymentSession.id,
{}
)
await service.capturePayment({
amount: 200,
payment_id: payment.id,
})
await service.completePaymentCollections(paymentCollection.id)
paymentCollection = await service.retrievePaymentCollection(
paymentCollection.id,
{ relations: ["payment_sessions", "payments.captures"] }
)
expect(paymentCollection).toEqual(
expect.objectContaining({
id: expect.any(String),
currency_code: "USD",
amount: 200,
// TODO
// authorized_amount: 200,
// status: "authorized",
region_id: "reg_123",
deleted_at: null,
completed_at: expect.any(Date),
payment_sessions: [
expect.objectContaining({
id: expect.any(String),
currency_code: "USD",
amount: 200,
provider_id: "system",
status: "authorized",
authorized_at: expect.any(Date),
}),
],
payments: [
expect.objectContaining({
id: expect.any(String),
amount: 200,
currency_code: "USD",
provider_id: "system",
captures: [
expect.objectContaining({
amount: 200,
}),
],
}),
],
})
)
})
})
describe("PaymentCollection", () => {
let service: IPaymentModuleService
let shutdownFunc: () => Promise<void>
afterAll(async () => {
await shutdownFunc()
})
beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
const repositoryManager = await MikroOrmWrapper.forkManager()
const initModulesConfig = getInitModuleConfig()
const { medusaApp, shutdown } = await initModules(initModulesConfig)
service = medusaApp.modules[Modules.PAYMENT]
shutdownFunc = shutdown
await createPaymentCollections(repositoryManager)
await createPaymentSessions(repositoryManager)
await createPayments(repositoryManager)
})
afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
await shutdownFunc()
})
describe("create", () => {
it("should throw an error when required params are not passed", async () => {
let error = await service
.createPaymentCollection([
.createPaymentCollections([
{
amount: 200,
region_id: "req_123",
@@ -71,7 +164,7 @@ describe("Payment Module Service", () => {
)
error = await service
.createPaymentCollection([
.createPaymentCollections([
{
currency_code: "USD",
region_id: "req_123",
@@ -84,7 +177,7 @@ describe("Payment Module Service", () => {
)
error = await service
.createPaymentCollection([
.createPaymentCollections([
{
currency_code: "USD",
amount: 200,
@@ -99,7 +192,7 @@ describe("Payment Module Service", () => {
it("should create a payment collection successfully", async () => {
const [createdPaymentCollection] =
await service.createPaymentCollection([
await service.createPaymentCollections([
{ currency_code: "USD", amount: 200, region_id: "reg_123" },
])
@@ -220,10 +313,10 @@ describe("Payment Module Service", () => {
describe("update", () => {
it("should update a Payment Collection", async () => {
await service.updatePaymentCollection({
await service.updatePaymentCollections({
id: "pay-col-id-2",
currency_code: "eur",
authorized_amount: 200,
region_id: "reg-2",
})
const collection = await service.retrievePaymentCollection(
@@ -233,84 +326,48 @@ describe("Payment Module Service", () => {
expect(collection).toEqual(
expect.objectContaining({
id: "pay-col-id-2",
authorized_amount: 200,
region_id: "reg-2",
currency_code: "eur",
})
)
})
})
describe("complete", () => {
it("should complete a Payment Collection", async () => {
await service.completePaymentCollections("pay-col-id-1")
const collection = await service.retrievePaymentCollection(
"pay-col-id-1"
)
expect(collection).toEqual(
expect.objectContaining({
id: "pay-col-id-1",
completed_at: expect.any(Date),
})
)
})
})
})
describe("PaymentSession", () => {
let repositoryManager: SqlEntityManager
let service: IPaymentModuleService
let shutdownFunc: () => Promise<void>
afterAll(async () => {
await shutdownFunc()
})
beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
repositoryManager = await MikroOrmWrapper.forkManager()
const repositoryManager = await MikroOrmWrapper.forkManager()
service = await initialize({
database: {
clientUrl: DB_URL,
schema: process.env.MEDUSA_PAYMNET_DB_SCHEMA,
},
})
const initModulesConfig = getInitModuleConfig()
const { medusaApp, shutdown } = await initModules(initModulesConfig)
service = medusaApp.modules[Modules.PAYMENT]
await createPaymentCollections(repositoryManager)
await createPaymentSessions(repositoryManager)
})
afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
})
describe("create", () => {
it("should create a payment session successfully", async () => {
const paymentCollection = await service.createPaymentSession(
"pay-col-id-1",
{
amount: 200,
provider_id: "manual",
currency_code: "usd",
}
)
expect(paymentCollection).toEqual(
expect.objectContaining({
id: "pay-col-id-1",
status: "not_paid",
payment_sessions: expect.arrayContaining([
{
id: expect.any(String),
data: null,
status: "pending",
authorized_at: null,
currency_code: "usd",
amount: 200,
provider_id: "manual",
payment_collection: expect.objectContaining({
id: paymentCollection.id,
}),
},
]),
})
)
})
})
})
describe("Payment", () => {
let repositoryManager: SqlEntityManager
beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
repositoryManager = await MikroOrmWrapper.forkManager()
service = await initialize({
database: {
clientUrl: DB_URL,
schema: process.env.MEDUSA_PAYMNET_DB_SCHEMA,
},
})
shutdownFunc = shutdown
await createPaymentCollections(repositoryManager)
await createPaymentSessions(repositoryManager)
@@ -319,61 +376,197 @@ describe("Payment Module Service", () => {
afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
await shutdownFunc()
})
describe("create", () => {
it("should create a payment successfully", async () => {
let paymentCollection = await service.createPaymentCollection({
currency_code: "usd",
amount: 200,
region_id: "reg",
it("should create a payment session successfully", async () => {
await service.createPaymentSession("pay-col-id-1", {
provider_id: "system",
providerContext: {
amount: 200,
currency_code: "usd",
payment_session_data: {},
context: {},
customer: {},
billing_address: {},
email: "test@test.test.com",
resource_id: "cart_test",
},
})
paymentCollection = await service.createPaymentSession(
paymentCollection.id,
{
amount: 200,
provider_id: "manual",
currency_code: "usd",
}
const paymentCollection = await service.retrievePaymentCollection(
"pay-col-id-1",
{ relations: ["payment_sessions"] }
)
const createdPayment = await service.createPayment({
data: {},
amount: 200,
provider_id: "manual",
currency_code: "usd",
payment_collection_id: paymentCollection.id,
payment_session_id: paymentCollection.payment_sessions![0].id,
expect(paymentCollection).toEqual(
expect.objectContaining({
id: "pay-col-id-1",
status: "not_paid",
payment_sessions: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
data: {},
status: "pending",
authorized_at: null,
currency_code: "usd",
amount: 200,
provider_id: "system",
}),
]),
})
)
})
})
describe("update", () => {
it("should update a payment session successfully", async () => {
let session = await service.createPaymentSession("pay-col-id-1", {
provider_id: "system",
providerContext: {
amount: 200,
currency_code: "usd",
payment_session_data: {},
context: {},
customer: {},
billing_address: {},
email: "test@test.test.com",
resource_id: "cart_test",
},
})
expect(createdPayment).toEqual(
session = await service.updatePaymentSession({
id: session.id,
providerContext: {
amount: 200,
currency_code: "eur",
resource_id: "res_id",
context: {},
customer: {},
billing_address: {},
email: "new@test.tsst",
payment_session_data: {},
},
})
expect(session).toEqual(
expect.objectContaining({
id: expect.any(String),
authorized_amount: null,
status: "pending",
currency_code: "eur",
amount: 200,
})
)
})
})
describe("authorize", () => {
it("should authorize a payment session", async () => {
const collection = await service.createPaymentCollections({
amount: 200,
region_id: "test-region",
currency_code: "usd",
})
const session = await service.createPaymentSession(collection.id, {
provider_id: "system",
providerContext: {
amount: 100,
currency_code: "usd",
payment_session_data: {},
context: {},
resource_id: "test",
email: "test@test.com",
billing_address: {},
customer: {},
},
})
const payment = await service.authorizePaymentSession(session.id, {})
expect(payment).toEqual(
expect.objectContaining({
id: expect.any(String),
amount: 100,
authorized_amount: 100,
currency_code: "usd",
provider_id: "system",
refunds: [],
captures: [],
data: {},
cart_id: null,
order_id: null,
order_edit_id: null,
customer_id: null,
data: {},
deleted_at: null,
captured_at: null,
canceled_at: null,
refunds: [],
captures: [],
amount: 200,
currency_code: "usd",
provider_id: "manual",
payment_collection: expect.objectContaining({
id: paymentCollection.id,
}),
payment_session: expect.objectContaining({
id: paymentCollection.payment_sessions![0].id,
id: expect.any(String),
}),
payment_session: {
id: expect.any(String),
currency_code: "usd",
amount: 100,
provider_id: "system",
data: {},
status: "authorized",
authorized_at: expect.any(Date),
payment_collection: expect.objectContaining({
id: expect.any(String),
}),
payment: expect.objectContaining({
authorized_amount: 100,
cart_id: null,
order_id: null,
order_edit_id: null,
customer_id: null,
data: {},
deleted_at: null,
captured_at: null,
canceled_at: null,
refunds: [],
captures: [],
amount: 100,
currency_code: "usd",
provider_id: "system",
}),
},
})
)
})
})
})
describe("Payment", () => {
let service: IPaymentModuleService
let shutdownFunc: () => Promise<void>
afterAll(async () => {
await shutdownFunc()
})
beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
const repositoryManager = await MikroOrmWrapper.forkManager()
const initModulesConfig = getInitModuleConfig()
const { medusaApp, shutdown } = await initModules(initModulesConfig)
service = medusaApp.modules[Modules.PAYMENT]
shutdownFunc = shutdown
await createPaymentCollections(repositoryManager)
await createPaymentSessions(repositoryManager)
await createPayments(repositoryManager)
})
afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
await shutdownFunc()
})
describe("update", () => {
it("should update a payment successfully", async () => {
@@ -417,83 +610,21 @@ describe("Payment Module Service", () => {
)
})
it("should capture payments in bulk successfully", async () => {
const capturedPayments = await service.capturePayment([
{
amount: 50, // partially captured
payment_id: "pay-id-1",
},
{
amount: 100, // fully captured
payment_id: "pay-id-2",
},
])
expect(capturedPayments).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "pay-id-1",
amount: 100,
authorized_amount: 100,
captured_at: null,
captures: [
expect.objectContaining({
created_by: null,
amount: 50,
}),
],
// captured_amount: 50,
}),
expect.objectContaining({
id: "pay-id-2",
amount: 100,
authorized_amount: 100,
// captured_at: expect.any(Date),
captures: [
expect.objectContaining({
created_by: null,
amount: 100,
}),
],
// captured_amount: 100,
}),
])
)
})
// TODO: uncomment when totals are implemented
// it("should fail to capture payments in bulk if one of the captures fail", async () => {
// const error = await service
// .capturePayment([
// {
// amount: 50,
// it("should fail to capture amount greater than authorized", async () => {
// const error = await service
// .capturePayment({
// amount: 200,
// payment_id: "pay-id-1",
// },
// {
// amount: 200, // exceeds authorized amount
// payment_id: "pay-id-2",
// },
// ])
// .catch((e) => e)
// })
// .catch((e) => e)
//
// expect(error.message).toEqual(
// "Total captured amount for payment: pay-id-2 exceeds authorized amount."
// )
// })
// it("should fail to capture amount greater than authorized", async () => {
// const error = await service
// .capturePayment({
// amount: 200,
// payment_id: "pay-id-1",
// })
// .catch((e) => e)
// expect(error.message).toEqual(
// "Total captured amount for payment: pay-id-1 exceeds authorised amount."
// )
// })
//
// expect(error.message).toEqual(
// "Total captured amount for payment: pay-id-1 exceeds authorized amount."
// )
// })
// it("should fail to capture already captured payment", async () => {
// await service.capturePayment({
// amount: 100,
@@ -507,60 +638,52 @@ describe("Payment Module Service", () => {
// })
// .catch((e) => e)
//
// expect(error.message).toEqual("The payment is already fully captured.")
// expect(error.message).toEqual(
// "The payment: pay-id-1 is already fully captured."
// )
// })
//
// it("should fail to capture a canceled payment", async () => {
// await service.cancelPayment("pay-id-1")
//
// const error = await service
// .capturePayment({
// amount: 100,
// payment_id: "pay-id-1",
// })
// .catch((e) => e)
//
// expect(error.message).toEqual(
// "The payment: pay-id-1 has been canceled."
// )
// })
})
describe("refund", () => {
it("should refund a payments in bulk successfully", async () => {
await service.capturePayment({
amount: 100,
payment_id: "pay-id-1",
})
it("should refund a payments successfully", async () => {
await service.capturePayment({
amount: 100,
payment_id: "pay-id-2",
})
const refundedPayment = await service.refundPayment([
{
amount: 100,
payment_id: "pay-id-1",
},
{
amount: 100,
payment_id: "pay-id-2",
},
])
const refundedPayment = await service.refundPayment({
amount: 100,
payment_id: "pay-id-2",
})
expect(refundedPayment).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "pay-id-1",
amount: 100,
refunds: [
expect.objectContaining({
created_by: null,
amount: 100,
}),
],
// captured_amount: 100,
// refunded_amount: 100,
}),
expect.objectContaining({
id: "pay-id-2",
amount: 100,
refunds: [
expect.objectContaining({
created_by: null,
amount: 100,
}),
],
// captured_amount: 100,
// refunded_amount: 100,
}),
])
expect.objectContaining({
id: "pay-id-2",
amount: 100,
refunds: [
expect.objectContaining({
created_by: null,
amount: 100,
}),
],
// captured_amount: 100,
// refunded_amount: 100,
})
)
})
@@ -582,5 +705,31 @@ describe("Payment Module Service", () => {
// )
// })
})
describe("cancel", () => {
it("should cancel a payment", async () => {
const payment = await service.cancelPayment("pay-id-2")
expect(payment).toEqual(
expect.objectContaining({
id: "pay-id-2",
canceled_at: expect.any(Date),
})
)
})
// TODO: revisit when totals are implemented
// it("should throw if trying to cancel a captured payment", async () => {
// await service.capturePayment({ payment_id: "pay-id-2", amount: 100 })
//
// const error = await service
// .cancelPayment("pay-id-2")
// .catch((e) => e.message)
//
// expect(error).toEqual(
// "Cannot cancel a payment: pay-id-2 that has been captured."
// )
// })
})
})
})