feat(payment): provider service (#6308)
This commit is contained in:
6
.changeset/swift-spoons-rescue.md
Normal file
6
.changeset/swift-spoons-rescue.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@medusajs/types": patch
|
||||
"@medusajs/utils": patch
|
||||
---
|
||||
|
||||
feat(types, utils): payment module - provider service
|
||||
@@ -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."
|
||||
// )
|
||||
// })
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
10
packages/payment/src/loaders/defaults.ts
Normal file
10
packages/payment/src/loaders/defaults.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { IPaymentModuleService, LoaderOptions } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export default async ({ container }: LoaderOptions): Promise<void> => {
|
||||
const paymentModuleService: IPaymentModuleService = container.resolve(
|
||||
ModuleRegistrationName.PAYMENT
|
||||
)
|
||||
|
||||
await paymentModuleService.createProvidersOnLoad()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
export * from "./connection"
|
||||
export * from "./container"
|
||||
export * from "./providers"
|
||||
|
||||
export * from "./defaults"
|
||||
|
||||
@@ -3,9 +3,11 @@ import { moduleProviderLoader } from "@medusajs/modules-sdk"
|
||||
import { LoaderOptions, ModuleProvider, ModulesSdkTypes } from "@medusajs/types"
|
||||
import { Lifetime, asFunction } from "awilix"
|
||||
|
||||
import * as providers from "../providers"
|
||||
|
||||
const registrationFn = async (klass, container, pluginOptions) => {
|
||||
container.register({
|
||||
[`payment_provider_${klass.prototype}`]: asFunction(
|
||||
[`pp_${klass.identifier}`]: asFunction(
|
||||
(cradle) => new klass(cradle, pluginOptions),
|
||||
{
|
||||
lifetime: klass.LIFE_TIME || Lifetime.SINGLETON,
|
||||
@@ -30,12 +32,14 @@ export default async ({
|
||||
| ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
|
||||
) & { providers: ModuleProvider[] }
|
||||
>): Promise<void> => {
|
||||
const pluginProviders =
|
||||
options?.providers?.filter((provider) => provider.resolve) || []
|
||||
// Local providers
|
||||
for (const provider of Object.values(providers)) {
|
||||
await registrationFn(provider, container, {})
|
||||
}
|
||||
|
||||
await moduleProviderLoader({
|
||||
container,
|
||||
providers: pluginProviders,
|
||||
providers: options?.providers || [],
|
||||
registerServiceFn: registrationFn,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -43,19 +43,21 @@ export default class PaymentCollection {
|
||||
})
|
||||
amount: number
|
||||
|
||||
@Property({
|
||||
columnType: "numeric",
|
||||
nullable: true,
|
||||
serializer: optionalNumericSerializer,
|
||||
})
|
||||
authorized_amount: number | null = null
|
||||
// TODO: make this computed properties
|
||||
|
||||
@Property({
|
||||
columnType: "numeric",
|
||||
nullable: true,
|
||||
serializer: optionalNumericSerializer,
|
||||
})
|
||||
refunded_amount: number | null = null
|
||||
// @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
|
||||
|
||||
@@ -16,7 +16,7 @@ import Payment from "./payment"
|
||||
|
||||
@Entity({ tableName: "payment_session" })
|
||||
export default class PaymentSession {
|
||||
[OptionalProps]?: "status"
|
||||
[OptionalProps]?: "status" | "data"
|
||||
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id: string
|
||||
@@ -33,8 +33,8 @@ export default class PaymentSession {
|
||||
@Property({ columnType: "text" })
|
||||
provider_id: string
|
||||
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
data: Record<string, unknown> | null = null
|
||||
@Property({ columnType: "jsonb" })
|
||||
data: Record<string, unknown> = {}
|
||||
|
||||
@Enum({
|
||||
items: () => PaymentSessionStatus,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { PaymentModuleService } from "@services"
|
||||
import loadConnection from "./loaders/connection"
|
||||
import loadContainer from "./loaders/container"
|
||||
import loadProviders from "./loaders/providers"
|
||||
import loadDefaults from "./loaders/defaults"
|
||||
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { ModulesSdkUtils } from "@medusajs/utils"
|
||||
@@ -25,7 +26,12 @@ export const revertMigration = ModulesSdkUtils.buildRevertMigrationScript(
|
||||
)
|
||||
|
||||
const service = PaymentModuleService
|
||||
const loaders = [loadContainer, loadConnection, loadProviders] as any
|
||||
const loaders = [
|
||||
loadContainer,
|
||||
loadConnection,
|
||||
loadProviders,
|
||||
loadDefaults,
|
||||
] as any
|
||||
|
||||
export const moduleDefinition: ModuleExports = {
|
||||
service,
|
||||
|
||||
1
packages/payment/src/providers/index.ts
Normal file
1
packages/payment/src/providers/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as SystemPaymentProvider } from "./system"
|
||||
71
packages/payment/src/providers/system.ts
Normal file
71
packages/payment/src/providers/system.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
PaymentProviderContext,
|
||||
PaymentProviderError,
|
||||
PaymentProviderSessionResponse,
|
||||
PaymentSessionStatus,
|
||||
} from "@medusajs/types"
|
||||
import { AbstractPaymentProvider } from "@medusajs/utils"
|
||||
|
||||
export class SystemProviderService extends AbstractPaymentProvider {
|
||||
static identifier = "system"
|
||||
|
||||
async getStatus(_): Promise<string> {
|
||||
return "authorized"
|
||||
}
|
||||
|
||||
async getPaymentData(_): Promise<Record<string, unknown>> {
|
||||
return {}
|
||||
}
|
||||
|
||||
async initiatePayment(
|
||||
context: PaymentProviderContext
|
||||
): Promise<PaymentProviderSessionResponse> {
|
||||
return { data: {} }
|
||||
}
|
||||
|
||||
async getPaymentStatus(
|
||||
paymentSessionData: Record<string, unknown>
|
||||
): Promise<PaymentSessionStatus> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
async retrievePayment(
|
||||
paymentSessionData: Record<string, unknown>
|
||||
): Promise<Record<string, unknown> | PaymentProviderError> {
|
||||
return {}
|
||||
}
|
||||
|
||||
async authorizePayment(_): Promise<
|
||||
| PaymentProviderError
|
||||
| {
|
||||
status: PaymentSessionStatus
|
||||
data: PaymentProviderSessionResponse["data"]
|
||||
}
|
||||
> {
|
||||
return { data: {}, status: PaymentSessionStatus.AUTHORIZED }
|
||||
}
|
||||
|
||||
async updatePayment(
|
||||
_
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse> {
|
||||
return { data: {} } as PaymentProviderSessionResponse
|
||||
}
|
||||
|
||||
async deletePayment(_): Promise<Record<string, unknown>> {
|
||||
return {}
|
||||
}
|
||||
|
||||
async capturePayment(_): Promise<Record<string, unknown>> {
|
||||
return {}
|
||||
}
|
||||
|
||||
async refundPayment(_): Promise<Record<string, unknown>> {
|
||||
return {}
|
||||
}
|
||||
|
||||
async cancelPayment(_): Promise<Record<string, unknown>> {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
export default SystemProviderService
|
||||
@@ -1 +1,2 @@
|
||||
export { default as PaymentModuleService } from "./payment-module"
|
||||
export { default as PaymentProviderService } from "./payment-provider"
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
CreateCaptureDTO,
|
||||
CreatePaymentCollectionDTO,
|
||||
CreatePaymentDTO,
|
||||
CreatePaymentProviderDTO,
|
||||
CreatePaymentSessionDTO,
|
||||
CreateRefundDTO,
|
||||
DAL,
|
||||
@@ -14,20 +15,18 @@ import {
|
||||
PaymentCollectionDTO,
|
||||
PaymentDTO,
|
||||
PaymentSessionDTO,
|
||||
PaymentSessionStatus,
|
||||
RefundDTO,
|
||||
SetPaymentSessionsDTO,
|
||||
UpdatePaymentCollectionDTO,
|
||||
UpdatePaymentDTO,
|
||||
UpdatePaymentSessionDTO,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
ModulesSdkUtils,
|
||||
MedusaError,
|
||||
InjectManager,
|
||||
ModulesSdkUtils,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
|
||||
import {
|
||||
Capture,
|
||||
Payment,
|
||||
@@ -36,6 +35,9 @@ import {
|
||||
Refund,
|
||||
} from "@models"
|
||||
|
||||
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
|
||||
import PaymentProviderService from "./payment-provider"
|
||||
|
||||
type InjectedDependencies = {
|
||||
baseRepository: DAL.RepositoryService
|
||||
paymentService: ModulesSdkTypes.InternalModuleService<any>
|
||||
@@ -43,9 +45,10 @@ type InjectedDependencies = {
|
||||
refundService: ModulesSdkTypes.InternalModuleService<any>
|
||||
paymentSessionService: ModulesSdkTypes.InternalModuleService<any>
|
||||
paymentCollectionService: ModulesSdkTypes.InternalModuleService<any>
|
||||
paymentProviderService: PaymentProviderService
|
||||
}
|
||||
|
||||
const generateMethodForModels = [PaymentCollection, PaymentSession]
|
||||
const generateMethodForModels = [PaymentCollection, Payment]
|
||||
|
||||
export default class PaymentModuleService<
|
||||
TPaymentCollection extends PaymentCollection = PaymentCollection,
|
||||
@@ -74,6 +77,7 @@ export default class PaymentModuleService<
|
||||
protected refundService_: ModulesSdkTypes.InternalModuleService<TRefund>
|
||||
protected paymentSessionService_: ModulesSdkTypes.InternalModuleService<TPaymentSession>
|
||||
protected paymentCollectionService_: ModulesSdkTypes.InternalModuleService<TPaymentCollection>
|
||||
protected paymentProviderService_: PaymentProviderService
|
||||
|
||||
constructor(
|
||||
{
|
||||
@@ -82,6 +86,7 @@ export default class PaymentModuleService<
|
||||
captureService,
|
||||
refundService,
|
||||
paymentSessionService,
|
||||
paymentProviderService,
|
||||
paymentCollectionService,
|
||||
}: InjectedDependencies,
|
||||
protected readonly moduleDeclaration: InternalModuleDeclaration
|
||||
@@ -95,6 +100,7 @@ export default class PaymentModuleService<
|
||||
this.captureService_ = captureService
|
||||
this.paymentService_ = paymentService
|
||||
this.paymentSessionService_ = paymentSessionService
|
||||
this.paymentProviderService_ = paymentProviderService
|
||||
this.paymentCollectionService_ = paymentCollectionService
|
||||
}
|
||||
|
||||
@@ -102,18 +108,18 @@ export default class PaymentModuleService<
|
||||
return joinerConfig
|
||||
}
|
||||
|
||||
createPaymentCollection(
|
||||
createPaymentCollections(
|
||||
data: CreatePaymentCollectionDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentCollectionDTO>
|
||||
|
||||
createPaymentCollection(
|
||||
createPaymentCollections(
|
||||
data: CreatePaymentCollectionDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentCollectionDTO[]>
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async createPaymentCollection(
|
||||
async createPaymentCollections(
|
||||
data: CreatePaymentCollectionDTO | CreatePaymentCollectionDTO[],
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<PaymentCollectionDTO | PaymentCollectionDTO[]> {
|
||||
@@ -132,17 +138,17 @@ export default class PaymentModuleService<
|
||||
)
|
||||
}
|
||||
|
||||
updatePaymentCollection(
|
||||
updatePaymentCollections(
|
||||
data: UpdatePaymentCollectionDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentCollectionDTO[]>
|
||||
updatePaymentCollection(
|
||||
updatePaymentCollections(
|
||||
data: UpdatePaymentCollectionDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentCollectionDTO>
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async updatePaymentCollection(
|
||||
async updatePaymentCollections(
|
||||
data: UpdatePaymentCollectionDTO | UpdatePaymentCollectionDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentCollectionDTO | PaymentCollectionDTO[]> {
|
||||
@@ -160,305 +166,361 @@ export default class PaymentModuleService<
|
||||
)
|
||||
}
|
||||
|
||||
createPayment(
|
||||
data: CreatePaymentDTO,
|
||||
completePaymentCollections(
|
||||
paymentCollectionId: string,
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO>
|
||||
createPayment(
|
||||
data: CreatePaymentDTO[],
|
||||
): Promise<PaymentCollectionDTO>
|
||||
completePaymentCollections(
|
||||
paymentCollectionId: string[],
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO[]>
|
||||
): Promise<PaymentCollectionDTO[]>
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async createPayment(
|
||||
data: CreatePaymentDTO | CreatePaymentDTO[],
|
||||
async completePaymentCollections(
|
||||
paymentCollectionId: string | string[],
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<PaymentDTO | PaymentDTO[]> {
|
||||
let input = Array.isArray(data) ? data : [data]
|
||||
): Promise<PaymentCollectionDTO | PaymentCollectionDTO[]> {
|
||||
const input = Array.isArray(paymentCollectionId)
|
||||
? paymentCollectionId.map((id) => ({
|
||||
id,
|
||||
completed_at: new Date(),
|
||||
}))
|
||||
: [{ id: paymentCollectionId, completed_at: new Date() }]
|
||||
|
||||
input = input.map((inputData) => ({
|
||||
payment_collection: inputData.payment_collection_id,
|
||||
payment_session: inputData.payment_session_id,
|
||||
...inputData,
|
||||
}))
|
||||
// TODO: what checks should be done here? e.g. captured_amount === amount?
|
||||
|
||||
const payments = await this.paymentService_.create(input, sharedContext)
|
||||
|
||||
return await this.baseRepository_.serialize<PaymentDTO[]>(
|
||||
Array.isArray(data) ? payments : payments[0],
|
||||
{
|
||||
populate: true,
|
||||
}
|
||||
const updated = await this.paymentCollectionService_.update(
|
||||
input,
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
|
||||
updatePayment(
|
||||
data: UpdatePaymentDTO,
|
||||
sharedContext?: Context | undefined
|
||||
): Promise<PaymentDTO>
|
||||
updatePayment(
|
||||
data: UpdatePaymentDTO[],
|
||||
sharedContext?: Context | undefined
|
||||
): Promise<PaymentDTO[]>
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async updatePayment(
|
||||
data: UpdatePaymentDTO | UpdatePaymentDTO[],
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<PaymentDTO | PaymentDTO[]> {
|
||||
const input = Array.isArray(data) ? data : [data]
|
||||
const result = await this.paymentService_.update(input, sharedContext)
|
||||
|
||||
return await this.baseRepository_.serialize<PaymentDTO[]>(
|
||||
Array.isArray(data) ? result : result[0],
|
||||
{
|
||||
populate: true,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
capturePayment(
|
||||
data: CreateCaptureDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO>
|
||||
capturePayment(
|
||||
data: CreateCaptureDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO[]>
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async capturePayment(
|
||||
data: CreateCaptureDTO | CreateCaptureDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<PaymentDTO | PaymentDTO[]> {
|
||||
const input = Array.isArray(data) ? data : [data]
|
||||
|
||||
const payments = await this.capturePaymentBulk_(input, sharedContext)
|
||||
|
||||
return await this.baseRepository_.serialize(
|
||||
Array.isArray(data) ? payments : payments[0],
|
||||
Array.isArray(paymentCollectionId) ? updated : updated[0],
|
||||
{ populate: true }
|
||||
)
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
protected async capturePaymentBulk_(
|
||||
data: CreateCaptureDTO[],
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<Payment[]> {
|
||||
let payments = await this.paymentService_.list(
|
||||
{ id: data.map((d) => d.payment_id) },
|
||||
{},
|
||||
sharedContext
|
||||
)
|
||||
const inputMap = new Map(data.map((d) => [d.payment_id, d]))
|
||||
|
||||
for (const payment of payments) {
|
||||
const input = inputMap.get(payment.id)!
|
||||
|
||||
if (payment.captured_at) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"The payment is already fully captured."
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: revisit when https://github.com/medusajs/medusa/pull/6253 is merged
|
||||
// if (payment.captured_amount + input.amount > payment.authorized_amount) {
|
||||
// throw new MedusaError(
|
||||
// MedusaError.Types.INVALID_DATA,
|
||||
// `Total captured amount for payment: ${payment.id} exceeds authorized amount.`
|
||||
// )
|
||||
// }
|
||||
}
|
||||
|
||||
await this.captureService_.create(
|
||||
data.map((d) => ({
|
||||
payment: d.payment_id,
|
||||
amount: d.amount,
|
||||
captured_by: d.captured_by,
|
||||
})),
|
||||
sharedContext
|
||||
)
|
||||
|
||||
let fullyCapturedPaymentsId: string[] = []
|
||||
for (const payment of payments) {
|
||||
const input = inputMap.get(payment.id)!
|
||||
|
||||
// TODO: revisit when https://github.com/medusajs/medusa/pull/6253 is merged
|
||||
// if (payment.captured_amount + input.amount === payment.amount) {
|
||||
// fullyCapturedPaymentsId.push(payment.id)
|
||||
// }
|
||||
}
|
||||
|
||||
if (fullyCapturedPaymentsId.length) {
|
||||
await this.paymentService_.update(
|
||||
fullyCapturedPaymentsId.map((id) => ({ id, captured_at: new Date() })),
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: set PaymentCollection status if fully captured
|
||||
|
||||
return await this.paymentService_.list(
|
||||
{ id: data.map((d) => d.payment_id) },
|
||||
{
|
||||
relations: ["captures"],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
|
||||
refundPayment(
|
||||
data: CreateRefundDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO>
|
||||
refundPayment(
|
||||
data: CreateRefundDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO[]>
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async refundPayment(
|
||||
data: CreateRefundDTO | CreateRefundDTO[],
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<PaymentDTO | PaymentDTO[]> {
|
||||
const input = Array.isArray(data) ? data : [data]
|
||||
|
||||
const payments = await this.refundPaymentBulk_(input, sharedContext)
|
||||
|
||||
return await this.baseRepository_.serialize(
|
||||
Array.isArray(data) ? payments : payments[0],
|
||||
{ populate: true }
|
||||
)
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async refundPaymentBulk_(
|
||||
data: CreateRefundDTO[],
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<Payment[]> {
|
||||
const payments = await this.paymentService_.list(
|
||||
{ id: data.map(({ payment_id }) => payment_id) },
|
||||
{},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const inputMap = new Map(data.map((d) => [d.payment_id, d]))
|
||||
|
||||
// TODO: revisit when https://github.com/medusajs/medusa/pull/6253 is merged
|
||||
// for (const payment of payments) {
|
||||
// const input = inputMap.get(payment.id)!
|
||||
// if (payment.captured_amount < input.amount) {
|
||||
// throw new MedusaError(
|
||||
// MedusaError.Types.INVALID_DATA,
|
||||
// `Refund amount for payment: ${payment.id} cannot be greater than the amount captured on the payment.`
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
await this.refundService_.create(
|
||||
data.map((d) => ({
|
||||
payment: d.payment_id,
|
||||
amount: d.amount,
|
||||
captured_by: d.created_by,
|
||||
})),
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return await this.paymentService_.list(
|
||||
{ id: data.map(({ payment_id }) => payment_id) },
|
||||
{
|
||||
relations: ["refunds"],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
|
||||
createPaymentSession(
|
||||
paymentCollectionId: string,
|
||||
data: CreatePaymentSessionDTO,
|
||||
sharedContext?: Context | undefined
|
||||
): Promise<PaymentCollectionDTO>
|
||||
createPaymentSession(
|
||||
paymentCollectionId: string,
|
||||
data: CreatePaymentSessionDTO[],
|
||||
sharedContext?: Context | undefined
|
||||
): Promise<PaymentCollectionDTO>
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async createPaymentSession(
|
||||
paymentCollectionId: string,
|
||||
data: CreatePaymentSessionDTO | CreatePaymentSessionDTO[],
|
||||
data: CreatePaymentSessionDTO,
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<PaymentCollectionDTO> {
|
||||
let input = Array.isArray(data) ? data : [data]
|
||||
): Promise<PaymentSessionDTO> {
|
||||
const sessionData = await this.paymentProviderService_.createSession(
|
||||
data.provider_id,
|
||||
data.providerContext
|
||||
)
|
||||
|
||||
input = input.map((inputData) => ({
|
||||
payment_collection: paymentCollectionId,
|
||||
...inputData,
|
||||
}))
|
||||
|
||||
await this.paymentSessionService_.create(input, sharedContext)
|
||||
|
||||
return await this.retrievePaymentCollection(
|
||||
paymentCollectionId,
|
||||
const created = await this.paymentSessionService_.create(
|
||||
{
|
||||
relations: ["payment_sessions"],
|
||||
provider_id: data.provider_id,
|
||||
amount: data.providerContext.amount,
|
||||
currency_code: data.providerContext.currency_code,
|
||||
payment_collection: paymentCollectionId,
|
||||
data: sessionData,
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return await this.baseRepository_.serialize(created, { populate: true })
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async updatePaymentSession(
|
||||
data: UpdatePaymentSessionDTO,
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<PaymentSessionDTO> {
|
||||
const session = await this.paymentSessionService_.retrieve(
|
||||
data.id,
|
||||
{ select: ["id", "data", "provider_id"] },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const sessionData = await this.paymentProviderService_.updateSession(
|
||||
session.provider_id,
|
||||
data.providerContext
|
||||
)
|
||||
|
||||
const updated = await this.paymentSessionService_.update(
|
||||
{
|
||||
id: session.id,
|
||||
amount: data.providerContext.amount,
|
||||
currency_code: data.providerContext.currency_code,
|
||||
data: sessionData,
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return await this.baseRepository_.serialize(updated[0], { populate: true })
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async deletePaymentSession(
|
||||
id: string,
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<void> {
|
||||
const session = await this.paymentSessionService_.retrieve(
|
||||
id,
|
||||
{ select: ["id", "data", "provider_id"] },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
await this.paymentProviderService_.deleteSession({
|
||||
provider_id: session.provider_id,
|
||||
data: session.data,
|
||||
})
|
||||
|
||||
await this.paymentSessionService_.delete(id, sharedContext)
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async authorizePaymentSession(
|
||||
id: string,
|
||||
context: Record<string, unknown>,
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<PaymentDTO> {
|
||||
const session = await this.paymentSessionService_.retrieve(
|
||||
id,
|
||||
{
|
||||
select: ["id", "data", "provider_id", "amount", "currency_code"],
|
||||
relations: ["payment_collection"],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
if (session.authorized_at) {
|
||||
const payment = await this.paymentService_.retrieve(
|
||||
{ session_id: session.id },
|
||||
{},
|
||||
sharedContext
|
||||
)
|
||||
return await this.baseRepository_.serialize(payment, { populate: true })
|
||||
}
|
||||
|
||||
const { data, status } =
|
||||
await this.paymentProviderService_.authorizePayment(
|
||||
{
|
||||
provider_id: session.provider_id,
|
||||
data: session.data,
|
||||
},
|
||||
context
|
||||
)
|
||||
|
||||
await this.paymentSessionService_.update(
|
||||
{
|
||||
id: session.id,
|
||||
data,
|
||||
status,
|
||||
authorized_at:
|
||||
status === PaymentSessionStatus.AUTHORIZED ? new Date() : null,
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
if (status !== PaymentSessionStatus.AUTHORIZED) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
`Session: ${session.id} is not authorized with the provider.`
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: update status on payment collection if authorized_amount === amount - depends on the BigNumber PR
|
||||
|
||||
const payment = await this.paymentService_.create(
|
||||
{
|
||||
amount: session.amount,
|
||||
currency_code: session.currency_code,
|
||||
authorized_amount: session.amount,
|
||||
payment_session: session.id,
|
||||
payment_collection: session.payment_collection!.id,
|
||||
provider_id: session.provider_id,
|
||||
// customer_id: context.customer.id,
|
||||
data,
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return await this.retrievePayment(payment.id, {}, sharedContext)
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async updatePayment(
|
||||
data: UpdatePaymentDTO,
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<PaymentDTO> {
|
||||
// NOTE: currently there is no update with the provider but maybe data could be updated
|
||||
const result = await this.paymentService_.update(data, sharedContext)
|
||||
|
||||
return await this.baseRepository_.serialize<PaymentDTO>(result[0], {
|
||||
populate: true,
|
||||
})
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async capturePayment(
|
||||
data: CreateCaptureDTO,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<PaymentDTO> {
|
||||
const payment = await this.paymentService_.retrieve(
|
||||
data.payment_id,
|
||||
{ select: ["id", "data", "provider_id"] },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
if (payment.canceled_at) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`The payment: ${payment.id} has been canceled.`
|
||||
)
|
||||
}
|
||||
|
||||
if (payment.captured_at) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`The payment: ${payment.id} is already fully captured.`
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: revisit when https://github.com/medusajs/medusa/pull/6253 is merged
|
||||
// if (payment.captured_amount + input.amount > payment.authorized_amount) {
|
||||
// throw new MedusaError(
|
||||
// MedusaError.Types.INVALID_DATA,
|
||||
// `Total captured amount for payment: ${payment.id} exceeds authorized amount.`
|
||||
// )
|
||||
// }
|
||||
|
||||
const paymentData = await this.paymentProviderService_.capturePayment({
|
||||
data: payment.data!,
|
||||
provider_id: payment.provider_id,
|
||||
})
|
||||
|
||||
await this.captureService_.create(
|
||||
{
|
||||
payment: data.payment_id,
|
||||
amount: data.amount,
|
||||
captured_by: data.captured_by,
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
await this.paymentService_.update(
|
||||
{ id: payment.id, data: paymentData },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
// TODO: revisit when https://github.com/medusajs/medusa/pull/6253 is merged
|
||||
// if (payment.captured_amount + data.amount === payment.amount) {
|
||||
// await this.paymentService_.update(
|
||||
// { id: payment.id, captured_at: new Date() },
|
||||
// sharedContext
|
||||
// )
|
||||
// }
|
||||
|
||||
return await this.retrievePayment(
|
||||
payment.id,
|
||||
{ relations: ["captures"] },
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async refundPayment(
|
||||
data: CreateRefundDTO,
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<PaymentDTO> {
|
||||
const payment = await this.paymentService_.retrieve(
|
||||
data.payment_id,
|
||||
{ select: ["id", "data", "provider_id"] },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
authorizePaymentCollection(
|
||||
paymentCollectionId: string,
|
||||
sharedContext?: Context | undefined
|
||||
): Promise<PaymentCollectionDTO> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
completePaymentCollection(
|
||||
paymentCollectionId: string,
|
||||
sharedContext?: Context | undefined
|
||||
): Promise<PaymentCollectionDTO> {
|
||||
throw new Error("Method not implemented.")
|
||||
// TODO: revisit when https://github.com/medusajs/medusa/pull/6253 is merged
|
||||
// if (payment.captured_amount < input.amount) {
|
||||
// throw new MedusaError(
|
||||
// MedusaError.Types.INVALID_DATA,
|
||||
// `Refund amount for payment: ${payment.id} cannot be greater than the amount captured on the payment.`
|
||||
// )
|
||||
// }
|
||||
|
||||
const paymentData = await this.paymentProviderService_.refundPayment(
|
||||
{
|
||||
data: payment.data!,
|
||||
provider_id: payment.provider_id,
|
||||
},
|
||||
data.amount
|
||||
)
|
||||
|
||||
await this.refundService_.create(
|
||||
{
|
||||
payment: data.payment_id,
|
||||
amount: data.amount,
|
||||
created_by: data.created_by,
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
await this.paymentService_.update({ id: payment.id, data: paymentData })
|
||||
|
||||
return await this.retrievePayment(
|
||||
payment.id,
|
||||
{ relations: ["refunds"] },
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
|
||||
cancelPayment(paymentId: string, sharedContext?: Context): Promise<PaymentDTO>
|
||||
cancelPayment(
|
||||
paymentId: string[],
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO[]>
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async cancelPayment(
|
||||
paymentId: string,
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<PaymentDTO> {
|
||||
const payment = await this.paymentService_.retrieve(
|
||||
paymentId,
|
||||
{ select: ["id", "data", "provider_id"] },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
cancelPayment(
|
||||
paymentId: string | string[],
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO | PaymentDTO[]> {
|
||||
throw new Error("Method not implemented.")
|
||||
// TODO: revisit when totals are implemented
|
||||
// if (payment.captured_amount !== 0) {
|
||||
// throw new MedusaError(
|
||||
// MedusaError.Types.INVALID_DATA,
|
||||
// `Cannot cancel a payment: ${payment.id} that has been captured.`
|
||||
// )
|
||||
// }
|
||||
|
||||
await this.paymentProviderService_.cancelPayment({
|
||||
data: payment.data!,
|
||||
provider_id: payment.provider_id,
|
||||
})
|
||||
|
||||
await this.paymentService_.update(
|
||||
{ id: paymentId, canceled_at: new Date() },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return await this.retrievePayment(payment.id, {}, sharedContext)
|
||||
}
|
||||
|
||||
authorizePaymentSessions(
|
||||
paymentCollectionId: string,
|
||||
sessionIds: string[],
|
||||
sharedContext?: Context | undefined
|
||||
): Promise<PaymentCollectionDTO> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
completePaymentSessions(
|
||||
paymentCollectionId: string,
|
||||
sessionIds: string[],
|
||||
sharedContext?: Context | undefined
|
||||
): Promise<PaymentCollectionDTO> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
setPaymentSessions(
|
||||
paymentCollectionId: string,
|
||||
data: SetPaymentSessionsDTO[],
|
||||
sharedContext?: Context | undefined
|
||||
): Promise<PaymentCollectionDTO> {
|
||||
throw new Error("Method not implemented.")
|
||||
async createProvidersOnLoad() {
|
||||
const providersToLoad = this.__container__["payment_providers"]
|
||||
|
||||
const providers = await this.paymentProviderService_.list({
|
||||
// @ts-ignore TODO
|
||||
id: providersToLoad.map((p) => p.getIdentifier()),
|
||||
})
|
||||
|
||||
const loadedProvidersMap = new Map(providers.map((p) => [p.id, p]))
|
||||
|
||||
const providersToCreate: CreatePaymentProviderDTO[] = []
|
||||
for (const provider of providersToLoad) {
|
||||
if (loadedProvidersMap.has(provider.getIdentifier())) {
|
||||
continue
|
||||
}
|
||||
|
||||
providersToCreate.push({
|
||||
id: provider.getIdentifier(),
|
||||
})
|
||||
}
|
||||
|
||||
await this.paymentProviderService_.create(providersToCreate)
|
||||
}
|
||||
}
|
||||
|
||||
183
packages/payment/src/services/payment-provider.ts
Normal file
183
packages/payment/src/services/payment-provider.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
import { EOL } from "os"
|
||||
import { isDefined, MedusaError } from "medusa-core-utils"
|
||||
import {
|
||||
Context,
|
||||
CreatePaymentProviderDTO,
|
||||
DAL,
|
||||
InternalModuleDeclaration,
|
||||
IPaymentProvider,
|
||||
PaymentProviderAuthorizeResponse,
|
||||
PaymentProviderContext,
|
||||
PaymentProviderDataInput,
|
||||
PaymentProviderError,
|
||||
PaymentProviderSessionResponse,
|
||||
PaymentSessionStatus,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
isPaymentProviderError,
|
||||
MedusaContext,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
import { PaymentProvider } from "@models"
|
||||
|
||||
type InjectedDependencies = {
|
||||
paymentProviderRepository: DAL.RepositoryService
|
||||
[key: `pp_${string}`]: IPaymentProvider
|
||||
}
|
||||
|
||||
export default class PaymentProviderService {
|
||||
protected readonly container_: InjectedDependencies
|
||||
protected readonly paymentProviderRepository_: DAL.RepositoryService
|
||||
|
||||
constructor(
|
||||
container: InjectedDependencies,
|
||||
|
||||
protected readonly moduleDeclaration: InternalModuleDeclaration
|
||||
) {
|
||||
this.container_ = container
|
||||
this.paymentProviderRepository_ = container.paymentProviderRepository
|
||||
}
|
||||
|
||||
@InjectTransactionManager("paymentProviderRepository_")
|
||||
async create(
|
||||
data: CreatePaymentProviderDTO[],
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<PaymentProvider[]> {
|
||||
return await this.paymentProviderRepository_.create(data, sharedContext)
|
||||
}
|
||||
|
||||
@InjectManager("paymentProviderRepository_")
|
||||
async list(
|
||||
@MedusaContext() sharedContext?: Context
|
||||
): Promise<PaymentProvider[]> {
|
||||
return await this.paymentProviderRepository_.find(undefined, sharedContext)
|
||||
}
|
||||
|
||||
retrieveProvider(providerId: string): IPaymentProvider {
|
||||
try {
|
||||
return this.container_[`pp_${providerId}`] as IPaymentProvider
|
||||
} catch (e) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Could not find a payment provider with id: ${providerId}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async createSession(
|
||||
providerId: string,
|
||||
sessionInput: PaymentProviderContext
|
||||
): Promise<PaymentProviderSessionResponse["data"]> {
|
||||
const provider = this.retrieveProvider(providerId)
|
||||
|
||||
if (
|
||||
!isDefined(sessionInput.currency_code) ||
|
||||
!isDefined(sessionInput.amount)
|
||||
) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_ARGUMENT,
|
||||
"`currency_code` and `amount` are required to create payment session."
|
||||
)
|
||||
}
|
||||
|
||||
const paymentResponse = await provider.initiatePayment(sessionInput)
|
||||
|
||||
if (isPaymentProviderError(paymentResponse)) {
|
||||
this.throwPaymentProviderError(paymentResponse)
|
||||
}
|
||||
|
||||
return (paymentResponse as PaymentProviderSessionResponse).data
|
||||
}
|
||||
|
||||
async updateSession(
|
||||
providerId: string,
|
||||
sessionInput: PaymentProviderContext
|
||||
): Promise<Record<string, unknown> | undefined> {
|
||||
const provider = this.retrieveProvider(providerId)
|
||||
|
||||
const paymentResponse = await provider.updatePayment(sessionInput)
|
||||
|
||||
if (isPaymentProviderError(paymentResponse)) {
|
||||
this.throwPaymentProviderError(paymentResponse)
|
||||
}
|
||||
|
||||
return (paymentResponse as PaymentProviderSessionResponse)?.data
|
||||
}
|
||||
|
||||
async deleteSession(input: PaymentProviderDataInput): Promise<void> {
|
||||
const provider = this.retrieveProvider(input.provider_id)
|
||||
|
||||
const error = await provider.deletePayment(input.data)
|
||||
if (isPaymentProviderError(error)) {
|
||||
this.throwPaymentProviderError(error)
|
||||
}
|
||||
}
|
||||
|
||||
async authorizePayment(
|
||||
input: PaymentProviderDataInput,
|
||||
context: Record<string, unknown>
|
||||
): Promise<{ data: Record<string, unknown>; status: PaymentSessionStatus }> {
|
||||
const provider = this.retrieveProvider(input.provider_id)
|
||||
|
||||
const res = await provider.authorizePayment(input.data, context)
|
||||
if (isPaymentProviderError(res)) {
|
||||
this.throwPaymentProviderError(res)
|
||||
}
|
||||
|
||||
const { data, status } = res as PaymentProviderAuthorizeResponse
|
||||
return { data, status }
|
||||
}
|
||||
|
||||
async getStatus(
|
||||
input: PaymentProviderDataInput
|
||||
): Promise<PaymentSessionStatus> {
|
||||
const provider = this.retrieveProvider(input.provider_id)
|
||||
return await provider.getPaymentStatus(input.data)
|
||||
}
|
||||
|
||||
async capturePayment(
|
||||
input: PaymentProviderDataInput
|
||||
): Promise<Record<string, unknown>> {
|
||||
const provider = this.retrieveProvider(input.provider_id)
|
||||
|
||||
const res = await provider.capturePayment(input.data)
|
||||
if (isPaymentProviderError(res)) {
|
||||
this.throwPaymentProviderError(res)
|
||||
}
|
||||
|
||||
return res as Record<string, unknown>
|
||||
}
|
||||
|
||||
async cancelPayment(input: PaymentProviderDataInput): Promise<void> {
|
||||
const provider = this.retrieveProvider(input.provider_id)
|
||||
|
||||
const error = await provider.cancelPayment(input.data)
|
||||
if (isPaymentProviderError(error)) {
|
||||
this.throwPaymentProviderError(error)
|
||||
}
|
||||
}
|
||||
|
||||
async refundPayment(
|
||||
input: PaymentProviderDataInput,
|
||||
amount: number
|
||||
): Promise<Record<string, unknown>> {
|
||||
const provider = this.retrieveProvider(input.provider_id)
|
||||
|
||||
const res = await provider.refundPayment(input.data, amount)
|
||||
if (isPaymentProviderError(res)) {
|
||||
this.throwPaymentProviderError(res)
|
||||
}
|
||||
|
||||
return res as Record<string, unknown>
|
||||
}
|
||||
|
||||
private throwPaymentProviderError(errObj: PaymentProviderError) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`${errObj.error}${errObj.detail ? `:${EOL}${errObj.detail}` : ""}`,
|
||||
errObj.code
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,34 @@ export enum PaymentCollectionStatus {
|
||||
CANCELED = "canceled",
|
||||
}
|
||||
|
||||
/**
|
||||
* @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",
|
||||
}
|
||||
|
||||
export interface PaymentCollectionDTO {
|
||||
/**
|
||||
* The ID of the Payment Collection
|
||||
@@ -255,6 +283,48 @@ export interface PaymentSessionDTO {
|
||||
* The ID of the Payment Session
|
||||
*/
|
||||
id: string
|
||||
|
||||
/**
|
||||
* The amount
|
||||
*/
|
||||
amount: number
|
||||
|
||||
/**
|
||||
* Payment session currency
|
||||
*/
|
||||
currency_code: string
|
||||
|
||||
/**
|
||||
* The ID of payment provider
|
||||
*/
|
||||
provider_id: string
|
||||
|
||||
/**
|
||||
* Payment provider data
|
||||
*/
|
||||
data: Record<string, unknown>
|
||||
|
||||
/**
|
||||
* The status of the payment session
|
||||
*/
|
||||
status: PaymentSessionStatus
|
||||
|
||||
/**
|
||||
* When the session was authorized
|
||||
*/
|
||||
authorized_at?: Date
|
||||
|
||||
/**
|
||||
* The payment collection the session is associated with
|
||||
* @expandable
|
||||
*/
|
||||
payment_collection?: PaymentCollectionDTO
|
||||
|
||||
/**
|
||||
* The payment created from the session
|
||||
* @expandable
|
||||
*/
|
||||
payment?: PaymentDTO
|
||||
}
|
||||
|
||||
export interface PaymentProviderDTO {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export * from "./common"
|
||||
export * from "./mutations"
|
||||
export * from "./provider"
|
||||
export * from "./service"
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { PaymentCollectionStatus } from "./common"
|
||||
import { PaymentProviderContext } from "./provider"
|
||||
|
||||
/**
|
||||
* Payment Collection
|
||||
@@ -26,6 +27,7 @@ export interface UpdatePaymentCollectionDTO
|
||||
|
||||
export interface CreatePaymentDTO {
|
||||
amount: number
|
||||
|
||||
currency_code: string
|
||||
provider_id: string
|
||||
data: Record<string, unknown>
|
||||
@@ -46,8 +48,6 @@ export interface UpdatePaymentDTO {
|
||||
order_id?: string
|
||||
order_edit_id?: string
|
||||
customer_id?: string
|
||||
|
||||
data?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface CreateCaptureDTO {
|
||||
@@ -69,17 +69,19 @@ export interface CreateRefundDTO {
|
||||
*/
|
||||
|
||||
export interface CreatePaymentSessionDTO {
|
||||
amount: number
|
||||
currency_code: string
|
||||
provider_id: string
|
||||
|
||||
cart_id?: string
|
||||
resource_id?: string
|
||||
customer_id?: string
|
||||
providerContext: PaymentProviderContext
|
||||
}
|
||||
|
||||
export interface SetPaymentSessionsDTO {
|
||||
provider_id: string
|
||||
amount: number
|
||||
session_id?: string
|
||||
export interface UpdatePaymentSessionDTO {
|
||||
id: string
|
||||
providerContext: PaymentProviderContext
|
||||
}
|
||||
|
||||
/**
|
||||
* Payment Provider
|
||||
*/
|
||||
export interface CreatePaymentProviderDTO {
|
||||
id: string
|
||||
is_enabled?: boolean
|
||||
}
|
||||
|
||||
212
packages/types/src/payment/provider.ts
Normal file
212
packages/types/src/payment/provider.ts
Normal file
@@ -0,0 +1,212 @@
|
||||
import { PaymentSessionStatus } from "./common"
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*
|
||||
* A payment's context.
|
||||
*/
|
||||
export type PaymentProviderContext = {
|
||||
/**
|
||||
* The payment's billing address.
|
||||
*/
|
||||
billing_address?: Record<string, unknown> | null // TODO: revisit types
|
||||
/**
|
||||
* The customer's email.
|
||||
*/
|
||||
email?: string
|
||||
/**
|
||||
* The selected currency code.
|
||||
*/
|
||||
currency_code: string
|
||||
/**
|
||||
* The payment's amount.
|
||||
*/
|
||||
amount: number
|
||||
/**
|
||||
* The ID of the resource the payment is associated with. For example, the cart's ID.
|
||||
*/
|
||||
resource_id: string
|
||||
/**
|
||||
* The customer associated with this payment.
|
||||
*/
|
||||
customer?: Record<string, unknown> // TODO: type
|
||||
/**
|
||||
* The context.
|
||||
*/
|
||||
context: Record<string, unknown>
|
||||
/**
|
||||
* If the payment session hasn't been created or initiated yet, it'll be an empty object.
|
||||
* If the payment session exists, it'll be the value of the payment session's `data` field.
|
||||
*/
|
||||
payment_session_data: Record<string, unknown>
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*
|
||||
* The response of operations on a payment.
|
||||
*/
|
||||
export type PaymentProviderSessionResponse = {
|
||||
/**
|
||||
* The data to be stored in the `data` field of the Payment Session to be created.
|
||||
* The `data` field is useful to hold any data required by the third-party provider to process the payment or retrieve its details at a later point.
|
||||
*/
|
||||
data: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type PaymentProviderAuthorizeResponse = {
|
||||
/**
|
||||
* The status of the payment, which will be stored in the payment session's `status` field.
|
||||
*/
|
||||
status: PaymentSessionStatus
|
||||
/**
|
||||
* The `data` to be stored in the payment session's `data` field.
|
||||
*/
|
||||
data: PaymentProviderSessionResponse["data"]
|
||||
}
|
||||
|
||||
export type PaymentProviderDataInput = {
|
||||
provider_id: string
|
||||
data: Record<string, unknown>
|
||||
}
|
||||
|
||||
/**
|
||||
* An object that is returned in case of an error.
|
||||
*/
|
||||
export interface PaymentProviderError {
|
||||
/**
|
||||
* The error message
|
||||
*/
|
||||
error: string
|
||||
/**
|
||||
* The error code.
|
||||
*/
|
||||
code?: string
|
||||
/**
|
||||
* Any additional helpful details.
|
||||
*/
|
||||
detail?: any
|
||||
}
|
||||
|
||||
export interface IPaymentProvider {
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* Return a unique identifier to retrieve the payment plugin provider
|
||||
*/
|
||||
getIdentifier(): string
|
||||
|
||||
/**
|
||||
* Make calls to the third-party provider to initialize the payment. For example, in Stripe this method is used to create a Payment Intent for the customer.
|
||||
*
|
||||
* @param {PaymentProviderContext} context - The context of the payment.
|
||||
* @returns {Promise<PaymentProviderError | PaymentProviderSessionResponse>} Either the payment's data or an error object.
|
||||
*/
|
||||
initiatePayment(
|
||||
context: PaymentProviderContext
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse>
|
||||
|
||||
/**
|
||||
* This method is used to update the payment session.
|
||||
*
|
||||
* @param {PaymentProviderContext} context - The context of the payment.
|
||||
* @returns {Promise<PaymentProviderError | PaymentProviderSessionResponse | void>} Either the payment's data or an error object.
|
||||
*/
|
||||
updatePayment(
|
||||
context: PaymentProviderContext
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse>
|
||||
|
||||
/**
|
||||
* This method is used to perform any actions necessary before a Payment Session is deleted. The Payment Session is deleted in one of the following cases:
|
||||
*
|
||||
* @param {Record<string, unknown>} paymentSessionData - The `data` field of the Payment Session.
|
||||
* @returns Either an error object or an empty object.
|
||||
*/
|
||||
deletePayment(
|
||||
paymentSessionData: Record<string, unknown>
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
||||
|
||||
/**
|
||||
* This method is used to authorize payment using the Payment Session.
|
||||
* You can interact with a third-party provider and perform any actions necessary to authorize the payment.
|
||||
*
|
||||
* The payment authorization might require additional action from the customer before it is declared authorized. Once that additional action is performed,
|
||||
* the `authorizePayment` method will be called again to validate that the payment is now fully authorized. So, make sure to implement it for this case as well, if necessary.
|
||||
*
|
||||
* :::note
|
||||
*
|
||||
* The payment authorization status is determined using the {@link getPaymentStatus} method. If the status is `requires_more`, then it means additional actions are required
|
||||
* from the customer.
|
||||
*
|
||||
* :::
|
||||
*
|
||||
* @param {Record<string, unknown>} paymentSessionData - The `data` field of the payment session.
|
||||
* @param {Record<string, unknown>} context - The context of the authorization.
|
||||
* @returns The authorization details or an error object.
|
||||
*/
|
||||
authorizePayment(
|
||||
paymentSessionData: Record<string, unknown>,
|
||||
context: Record<string, unknown>
|
||||
): Promise<PaymentProviderError | PaymentProviderAuthorizeResponse>
|
||||
|
||||
/**
|
||||
* This method is used to capture the payment amount. This is typically triggered manually by the store operator from the admin.
|
||||
*
|
||||
* You can utilize this method to interact with the third-party provider and perform any actions necessary to capture the payment.
|
||||
*
|
||||
* @param {Record<string, unknown>} paymentSessionData - The `data` field of the Payment for its first parameter.
|
||||
* @returns Either an error object or a value that's stored in the `data` field of the Payment.
|
||||
*/
|
||||
capturePayment(
|
||||
paymentSessionData: Record<string, unknown>
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
||||
|
||||
/**
|
||||
* This method is used to refund a payment. This is typically triggered manually by the store operator from the admin. The refund amount might be the total amount or part of it.
|
||||
*
|
||||
* You can utilize this method to interact with the third-party provider and perform any actions necessary to refund the payment.
|
||||
*
|
||||
* @param {Record<string, unknown>} paymentSessionData - The `data` field of a Payment.
|
||||
* @param {number} refundAmount - the amount to refund.
|
||||
* @returns Either an error object or a value that's stored in the `data` field of the Payment.
|
||||
*/
|
||||
refundPayment(
|
||||
paymentSessionData: Record<string, unknown>,
|
||||
refundAmount: number
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
||||
|
||||
/**
|
||||
* This method is used to provide a uniform way of retrieving the payment information from the third-party provider.
|
||||
* For example, in Stripe’s Payment Provider this method is used to retrieve the payment intent details from Stripe.
|
||||
*
|
||||
* @param {Record<string, unknown>} paymentSessionData -
|
||||
* The `data` field of a Payment Session. Make sure to store in the `data` field any necessary data that would allow you to retrieve the payment data from the third-party provider.
|
||||
* @returns {Promise<PaymentProviderError | PaymentProviderSessionResponse["session_data"]>} The payment's data, typically retrieved from a third-party provider.
|
||||
*/
|
||||
retrievePayment(
|
||||
paymentSessionData: Record<string, unknown>
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
||||
|
||||
/**
|
||||
* This method is used to cancel a payment. This method is typically triggered by one of the following situations:
|
||||
*
|
||||
* You can utilize this method to interact with the third-party provider and perform any actions necessary to cancel the payment.
|
||||
*
|
||||
* @param {Record<string, unknown>} paymentSessionData - The `data` field of the Payment.
|
||||
* @returns Either an error object or a value that's stored in the `data` field of the Payment.
|
||||
*/
|
||||
cancelPayment(
|
||||
paymentSessionData: Record<string, unknown>
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
||||
|
||||
/**
|
||||
* This method is used to get the status of a Payment or a Payment Session.
|
||||
*
|
||||
* @param {Record<string, unknown>} paymentSessionData -
|
||||
* The `data` field of a Payment as a parameter. You can use this data to interact with the third-party provider to check the status of the payment if necessary.
|
||||
* @returns {Promise<PaymentSessionStatus>} The status of the Payment or Payment Session.
|
||||
*/
|
||||
getPaymentStatus(
|
||||
paymentSessionData: Record<string, unknown>
|
||||
): Promise<PaymentSessionStatus>
|
||||
}
|
||||
@@ -6,25 +6,26 @@ import {
|
||||
CreatePaymentDTO,
|
||||
CreatePaymentSessionDTO,
|
||||
CreateRefundDTO,
|
||||
SetPaymentSessionsDTO,
|
||||
UpdatePaymentCollectionDTO,
|
||||
UpdatePaymentDTO,
|
||||
UpdatePaymentSessionDTO,
|
||||
} from "./mutations"
|
||||
import {
|
||||
FilterablePaymentCollectionProps,
|
||||
PaymentCollectionDTO,
|
||||
PaymentDTO,
|
||||
PaymentSessionDTO,
|
||||
} from "./common"
|
||||
import { FindConfig } from "../common"
|
||||
|
||||
export interface IPaymentModuleService extends IModuleService {
|
||||
/* ********** PAYMENT COLLECTION ********** */
|
||||
|
||||
createPaymentCollection(
|
||||
createPaymentCollections(
|
||||
data: CreatePaymentCollectionDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentCollectionDTO[]>
|
||||
createPaymentCollection(
|
||||
createPaymentCollections(
|
||||
data: CreatePaymentCollectionDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentCollectionDTO>
|
||||
@@ -47,11 +48,11 @@ export interface IPaymentModuleService extends IModuleService {
|
||||
sharedContext?: Context
|
||||
): Promise<[PaymentCollectionDTO[], number]>
|
||||
|
||||
updatePaymentCollection(
|
||||
updatePaymentCollections(
|
||||
data: UpdatePaymentCollectionDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentCollectionDTO[]>
|
||||
updatePaymentCollection(
|
||||
updatePaymentCollections(
|
||||
data: UpdatePaymentCollectionDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentCollectionDTO>
|
||||
@@ -65,59 +66,14 @@ export interface IPaymentModuleService extends IModuleService {
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
authorizePaymentCollection(
|
||||
completePaymentCollections(
|
||||
paymentCollectionId: string,
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentCollectionDTO>
|
||||
|
||||
completePaymentCollection(
|
||||
paymentCollectionId: string,
|
||||
completePaymentCollections(
|
||||
paymentCollectionId: string[],
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentCollectionDTO>
|
||||
|
||||
/* ********** PAYMENT ********** */
|
||||
|
||||
createPayment(
|
||||
data: CreatePaymentDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO>
|
||||
createPayment(
|
||||
data: CreatePaymentDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO[]>
|
||||
|
||||
capturePayment(
|
||||
data: CreateCaptureDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO>
|
||||
capturePayment(
|
||||
data: CreateCaptureDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO[]>
|
||||
|
||||
refundPayment(
|
||||
data: CreateRefundDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO>
|
||||
refundPayment(
|
||||
data: CreateRefundDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO[]>
|
||||
|
||||
cancelPayment(paymentId: string, sharedContext?: Context): Promise<PaymentDTO>
|
||||
cancelPayment(
|
||||
paymentId: string[],
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO[]>
|
||||
|
||||
updatePayment(
|
||||
data: UpdatePaymentDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO>
|
||||
updatePayment(
|
||||
data: UpdatePaymentDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO[]>
|
||||
): Promise<PaymentCollectionDTO[]>
|
||||
|
||||
/* ********** PAYMENT SESSION ********** */
|
||||
|
||||
@@ -125,28 +81,39 @@ export interface IPaymentModuleService extends IModuleService {
|
||||
paymentCollectionId: string,
|
||||
data: CreatePaymentSessionDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentCollectionDTO>
|
||||
createPaymentSession(
|
||||
paymentCollectionId: string,
|
||||
data: CreatePaymentSessionDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentCollectionDTO>
|
||||
): Promise<PaymentSessionDTO>
|
||||
|
||||
authorizePaymentSessions(
|
||||
paymentCollectionId: string,
|
||||
sessionIds: string[],
|
||||
updatePaymentSession(
|
||||
data: UpdatePaymentSessionDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentCollectionDTO>
|
||||
): Promise<PaymentSessionDTO>
|
||||
|
||||
completePaymentSessions(
|
||||
paymentCollectionId: string,
|
||||
sessionIds: string[],
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentCollectionDTO>
|
||||
deletePaymentSession(id: string, sharedContext?: Context): Promise<void>
|
||||
|
||||
setPaymentSessions(
|
||||
paymentCollectionId: string,
|
||||
data: SetPaymentSessionsDTO[],
|
||||
authorizePaymentSession(
|
||||
id: string,
|
||||
context: Record<string, unknown>,
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentCollectionDTO>
|
||||
): Promise<PaymentDTO>
|
||||
|
||||
/* ********** PAYMENT ********** */
|
||||
|
||||
updatePayment(
|
||||
data: UpdatePaymentDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO>
|
||||
|
||||
capturePayment(
|
||||
data: CreateCaptureDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO>
|
||||
|
||||
refundPayment(
|
||||
data: CreateRefundDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<PaymentDTO>
|
||||
|
||||
cancelPayment(paymentId: string, sharedContext?: Context): Promise<PaymentDTO>
|
||||
|
||||
createProvidersOnLoad(): Promise<void>
|
||||
}
|
||||
|
||||
127
packages/utils/src/payment/abstract-payment-provider.ts
Normal file
127
packages/utils/src/payment/abstract-payment-provider.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import {
|
||||
IPaymentProvider,
|
||||
MedusaContainer,
|
||||
PaymentProviderContext,
|
||||
PaymentProviderError,
|
||||
PaymentProviderSessionResponse,
|
||||
PaymentSessionStatus,
|
||||
} from "@medusajs/types"
|
||||
|
||||
export abstract class AbstractPaymentProvider implements IPaymentProvider {
|
||||
/**
|
||||
* You can use the `constructor` of your Payment Provider to have access to different services in Medusa through [dependency injection](https://docs.medusajs.com/development/fundamentals/dependency-injection).
|
||||
*
|
||||
* You can also use the constructor to initialize your integration with the third-party provider. For example, if you use a client to connect to the third-party provider’s APIs,
|
||||
* you can initialize it in the constructor and use it in other methods in the service.
|
||||
*
|
||||
* Additionally, if you’re creating your Payment Provider as an external plugin to be installed on any Medusa backend and you want to access the options added for the plugin,
|
||||
* you can access it in the constructor. The options are passed as a second parameter.
|
||||
*
|
||||
* @param {MedusaContainer} container - An instance of `MedusaContainer` that allows you to access other resources, such as services, in your Medusa backend through [dependency injection](https://docs.medusajs.com/development/fundamentals/dependency-injection)
|
||||
* @param {Record<string, unknown>} config - If this fulfillment provider is created in a plugin, the plugin's options are passed in this parameter.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* class MyPaymentService extends AbstractPaymentProvider {
|
||||
* // ...
|
||||
* constructor(container, options) {
|
||||
* super(container)
|
||||
* // you can access options here
|
||||
*
|
||||
* // you can also initialize a client that
|
||||
* // communicates with a third-party service.
|
||||
* this.client = new Client(options)
|
||||
* }
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
protected constructor(
|
||||
protected readonly container: MedusaContainer,
|
||||
protected readonly config?: Record<string, unknown> // eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
) {}
|
||||
|
||||
static _isPaymentProvider = true
|
||||
|
||||
static isPaymentProvider(object): boolean {
|
||||
return object?.constructor?._isPaymentProvider
|
||||
}
|
||||
|
||||
/**
|
||||
* The `PaymentProvider` entity has 2 properties: `id` and `is_installed`. The `identifier` property in the payment provider service is used when the payment provider is added to the database.
|
||||
*
|
||||
* The value of this property is also used to reference the payment provider throughout Medusa.
|
||||
* For example, it is used to [add a payment provider](https://docs.medusajs.com/api/admin#regions_postregionsregionpaymentproviders) to a region.
|
||||
*
|
||||
* ```ts
|
||||
* class MyPaymentService extends AbstractPaymentProvider {
|
||||
* static identifier = "my-payment"
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
public static identifier: string
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* Return a unique identifier to retrieve the payment plugin provider
|
||||
*/
|
||||
public getIdentifier(): string {
|
||||
const ctr = this.constructor as typeof AbstractPaymentProvider
|
||||
|
||||
if (!ctr.identifier) {
|
||||
throw new Error(`Missing static property "identifier".`)
|
||||
}
|
||||
|
||||
return ctr.identifier
|
||||
}
|
||||
|
||||
abstract capturePayment(
|
||||
paymentSessionData: Record<string, unknown>
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
||||
|
||||
abstract authorizePayment(
|
||||
paymentSessionData: Record<string, unknown>,
|
||||
context: Record<string, unknown>
|
||||
): Promise<
|
||||
| PaymentProviderError
|
||||
| {
|
||||
status: PaymentSessionStatus
|
||||
data: PaymentProviderSessionResponse["data"]
|
||||
}
|
||||
>
|
||||
|
||||
abstract cancelPayment(
|
||||
paymentSessionData: Record<string, unknown>
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
||||
|
||||
abstract initiatePayment(
|
||||
context: PaymentProviderContext
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse>
|
||||
|
||||
abstract deletePayment(
|
||||
paymentSessionData: Record<string, unknown>
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
||||
|
||||
abstract getPaymentStatus(
|
||||
paymentSessionData: Record<string, unknown>
|
||||
): Promise<PaymentSessionStatus>
|
||||
|
||||
abstract refundPayment(
|
||||
paymentSessionData: Record<string, unknown>,
|
||||
refundAmount: number
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
||||
|
||||
abstract retrievePayment(
|
||||
paymentSessionData: Record<string, unknown>
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
||||
|
||||
abstract updatePayment(
|
||||
context: PaymentProviderContext
|
||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse>
|
||||
}
|
||||
|
||||
export function isPaymentProviderError(obj: any): obj is PaymentProviderError {
|
||||
return obj && typeof obj === "object" && obj.error && obj.code && obj.detail
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from "./payment-collection"
|
||||
export * from "./payment-session"
|
||||
export * from "./abstract-payment-provider"
|
||||
|
||||
Reference in New Issue
Block a user