feat: Add support for managing account holder in payment module (#11015)
This commit is contained in:
1 parent
a37a9c8023
commit
59cbc0ec77
29 files changed
+1328
-803
No files matched your search
@@ -57,13 +57,13 @@ export const THREE_DAYS = 60 * 60 * 24 * 3
|
|||||||
|
|
||||||
export const completeCartWorkflowId = "complete-cart"
|
export const completeCartWorkflowId = "complete-cart"
|
||||||
/**
|
/**
|
||||||
* This workflow completes a cart and places an order for the customer. It's executed by the
|
* This workflow completes a cart and places an order for the customer. It's executed by the
|
||||||
* [Complete Cart Store API Route](https://docs.medusajs.com/api/store#carts_postcartsidcomplete).
|
* [Complete Cart Store API Route](https://docs.medusajs.com/api/store#carts_postcartsidcomplete).
|
||||||
*
|
*
|
||||||
* You can use this workflow within your own customizations or custom workflows, allowing you to wrap custom logic around completing a cart.
|
* You can use this workflow within your own customizations or custom workflows, allowing you to wrap custom logic around completing a cart.
|
||||||
* For example, in the [Subscriptions recipe](https://docs.medusajs.com/resources/recipes/subscriptions/examples/standard#create-workflow),
|
* For example, in the [Subscriptions recipe](https://docs.medusajs.com/resources/recipes/subscriptions/examples/standard#create-workflow),
|
||||||
* this workflow is used within another workflow that creates a subscription order.
|
* this workflow is used within another workflow that creates a subscription order.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const { result } = await completeCartWorkflow(container)
|
* const { result } = await completeCartWorkflow(container)
|
||||||
* .run({
|
* .run({
|
||||||
@@ -71,11 +71,11 @@ export const completeCartWorkflowId = "complete-cart"
|
|||||||
* id: "cart_123"
|
* id: "cart_123"
|
||||||
* }
|
* }
|
||||||
* })
|
* })
|
||||||
*
|
*
|
||||||
* @summary
|
* @summary
|
||||||
*
|
*
|
||||||
* Complete a cart and place an order.
|
* Complete a cart and place an order.
|
||||||
*
|
*
|
||||||
* @property hooks.validate - This hook is executed before all operations. You can consume this hook to perform any custom validation. If validation fails, you can throw an error to stop the workflow execution.
|
* @property hooks.validate - This hook is executed before all operations. You can consume this hook to perform any custom validation. If validation fails, you can throw an error to stop the workflow execution.
|
||||||
*/
|
*/
|
||||||
export const completeCartWorkflow = createWorkflow(
|
export const completeCartWorkflow = createWorkflow(
|
||||||
@@ -118,7 +118,6 @@ export const completeCartWorkflow = createWorkflow(
|
|||||||
// We choose the first payment session, as there will only be one active payment session
|
// We choose the first payment session, as there will only be one active payment session
|
||||||
// This might change in the future.
|
// This might change in the future.
|
||||||
id: paymentSessions[0].id,
|
id: paymentSessions[0].id,
|
||||||
context: { cart_id: cart.id },
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const { variants, sales_channel_id } = transform({ cart }, (data) => {
|
const { variants, sales_channel_id } = transform({ cart }, (data) => {
|
||||||
|
|||||||
@@ -26,14 +26,14 @@ export type ThrowUnlessPaymentCollectionNotePaidInput = {
|
|||||||
/**
|
/**
|
||||||
* This step validates that the payment collection is not paid. If not valid,
|
* This step validates that the payment collection is not paid. If not valid,
|
||||||
* the step will throw an error.
|
* the step will throw an error.
|
||||||
*
|
*
|
||||||
* :::note
|
* :::note
|
||||||
*
|
*
|
||||||
* You can retrieve a payment collection's details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query),
|
* You can retrieve a payment collection's details using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query),
|
||||||
* or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep).
|
* or [useQueryGraphStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/useQueryGraphStep).
|
||||||
*
|
*
|
||||||
* :::
|
* :::
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const data = throwUnlessPaymentCollectionNotPaid({
|
* const data = throwUnlessPaymentCollectionNotPaid({
|
||||||
* paymentCollection: {
|
* paymentCollection: {
|
||||||
@@ -77,10 +77,10 @@ export const markPaymentCollectionAsPaidId = "mark-payment-collection-as-paid"
|
|||||||
/**
|
/**
|
||||||
* This workflow marks a payment collection for an order as paid. It's used by the
|
* This workflow marks a payment collection for an order as paid. It's used by the
|
||||||
* [Mark Payment Collection as Paid Admin API Route](https://docs.medusajs.com/api/admin#payment-collections_postpaymentcollectionsidmarkaspaid).
|
* [Mark Payment Collection as Paid Admin API Route](https://docs.medusajs.com/api/admin#payment-collections_postpaymentcollectionsidmarkaspaid).
|
||||||
*
|
*
|
||||||
* You can use this workflow within your customizations or your own custom workflows, allowing you to wrap custom logic around
|
* You can use this workflow within your customizations or your own custom workflows, allowing you to wrap custom logic around
|
||||||
* marking a payment collection for an order as paid.
|
* marking a payment collection for an order as paid.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const { result } = await markPaymentCollectionAsPaid(container)
|
* const { result } = await markPaymentCollectionAsPaid(container)
|
||||||
* .run({
|
* .run({
|
||||||
@@ -89,16 +89,14 @@ export const markPaymentCollectionAsPaidId = "mark-payment-collection-as-paid"
|
|||||||
* payment_collection_id: "paycol_123",
|
* payment_collection_id: "paycol_123",
|
||||||
* }
|
* }
|
||||||
* })
|
* })
|
||||||
*
|
*
|
||||||
* @summary
|
* @summary
|
||||||
*
|
*
|
||||||
* Mark a payment collection for an order as paid.
|
* Mark a payment collection for an order as paid.
|
||||||
*/
|
*/
|
||||||
export const markPaymentCollectionAsPaid = createWorkflow(
|
export const markPaymentCollectionAsPaid = createWorkflow(
|
||||||
markPaymentCollectionAsPaidId,
|
markPaymentCollectionAsPaidId,
|
||||||
(
|
(input: WorkflowData<MarkPaymentCollectionAsPaidInput>) => {
|
||||||
input: WorkflowData<MarkPaymentCollectionAsPaidInput>
|
|
||||||
) => {
|
|
||||||
const paymentCollection = useRemoteQueryStep({
|
const paymentCollection = useRemoteQueryStep({
|
||||||
entry_point: "payment_collection",
|
entry_point: "payment_collection",
|
||||||
fields: ["id", "status", "amount"],
|
fields: ["id", "status", "amount"],
|
||||||
@@ -120,7 +118,6 @@ export const markPaymentCollectionAsPaid = createWorkflow(
|
|||||||
|
|
||||||
const payment = authorizePaymentSessionStep({
|
const payment = authorizePaymentSessionStep({
|
||||||
id: paymentSession.id,
|
id: paymentSession.id,
|
||||||
context: { order_id: input.order_id },
|
|
||||||
})
|
})
|
||||||
|
|
||||||
capturePaymentWorkflow.runAsStep({
|
capturePaymentWorkflow.runAsStep({
|
||||||
|
|||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
import {
|
||||||
|
IPaymentModuleService,
|
||||||
|
CreateAccountHolderDTO,
|
||||||
|
} from "@medusajs/framework/types"
|
||||||
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
|
||||||
|
|
||||||
|
export const createPaymentAccountHolderStepId = "create-payment-account-holder"
|
||||||
|
/**
|
||||||
|
* This step creates the account holder in the payment provider.
|
||||||
|
*/
|
||||||
|
export const createPaymentAccountHolderStep = createStep(
|
||||||
|
createPaymentAccountHolderStepId,
|
||||||
|
async (data: CreateAccountHolderDTO, { container }) => {
|
||||||
|
const service = container.resolve<IPaymentModuleService>(Modules.PAYMENT)
|
||||||
|
|
||||||
|
const accountHolder = await service.createAccountHolder(data)
|
||||||
|
|
||||||
|
return new StepResponse(accountHolder, accountHolder)
|
||||||
|
},
|
||||||
|
async (createdAccountHolder, { container }) => {
|
||||||
|
if (!createdAccountHolder) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const service = container.resolve<IPaymentModuleService>(Modules.PAYMENT)
|
||||||
|
await service.deleteAccountHolder(createdAccountHolder.id)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -24,7 +24,7 @@ export interface CreatePaymentSessionStepInput {
|
|||||||
amount: BigNumberInput
|
amount: BigNumberInput
|
||||||
/**
|
/**
|
||||||
* The currency code of the payment session.
|
* The currency code of the payment session.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* usd
|
* usd
|
||||||
*/
|
*/
|
||||||
@@ -42,7 +42,7 @@ export interface CreatePaymentSessionStepInput {
|
|||||||
|
|
||||||
export const createPaymentSessionStepId = "create-payment-session"
|
export const createPaymentSessionStepId = "create-payment-session"
|
||||||
/**
|
/**
|
||||||
* This step creates a payment session.
|
* This step creates a payment session.
|
||||||
*/
|
*/
|
||||||
export const createPaymentSessionStep = createStep(
|
export const createPaymentSessionStep = createStep(
|
||||||
createPaymentSessionStepId,
|
createPaymentSessionStepId,
|
||||||
|
|||||||
@@ -5,3 +5,4 @@ export * from "./delete-refund-reasons"
|
|||||||
export * from "./update-payment-collection"
|
export * from "./update-payment-collection"
|
||||||
export * from "./update-refund-reasons"
|
export * from "./update-refund-reasons"
|
||||||
export * from "./validate-deleted-payment-sessions"
|
export * from "./validate-deleted-payment-sessions"
|
||||||
|
export * from "./create-payment-account-holder"
|
||||||
+95
-10
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
PaymentProviderContext,
|
AccountHolderDTO,
|
||||||
|
CustomerDTO,
|
||||||
PaymentSessionDTO,
|
PaymentSessionDTO,
|
||||||
} from "@medusajs/framework/types"
|
} from "@medusajs/framework/types"
|
||||||
import {
|
import {
|
||||||
@@ -8,10 +9,15 @@ import {
|
|||||||
createWorkflow,
|
createWorkflow,
|
||||||
parallelize,
|
parallelize,
|
||||||
transform,
|
transform,
|
||||||
|
when,
|
||||||
} from "@medusajs/framework/workflows-sdk"
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
import { useRemoteQueryStep } from "../../common"
|
import { createRemoteLinkStep, useRemoteQueryStep } from "../../common"
|
||||||
import { createPaymentSessionStep } from "../steps"
|
import {
|
||||||
|
createPaymentSessionStep,
|
||||||
|
createPaymentAccountHolderStep,
|
||||||
|
} from "../steps"
|
||||||
import { deletePaymentSessionsWorkflow } from "./delete-payment-sessions"
|
import { deletePaymentSessionsWorkflow } from "./delete-payment-sessions"
|
||||||
|
import { isPresent, Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The data to create payment sessions.
|
* The data to create payment sessions.
|
||||||
@@ -26,25 +32,31 @@ export interface CreatePaymentSessionsWorkflowInput {
|
|||||||
* This provider is used to later process the payment sessions and their payments.
|
* This provider is used to later process the payment sessions and their payments.
|
||||||
*/
|
*/
|
||||||
provider_id: string
|
provider_id: string
|
||||||
|
/**
|
||||||
|
* The ID of the customer that the payment session should be associated with.
|
||||||
|
*/
|
||||||
|
customer_id?: string
|
||||||
/**
|
/**
|
||||||
* Custom data relevant for the payment provider to process the payment session.
|
* Custom data relevant for the payment provider to process the payment session.
|
||||||
* Learn more in [this documentation](https://docs.medusajs.com/resources/commerce-modules/payment/payment-session#data-property).
|
* Learn more in [this documentation](https://docs.medusajs.com/resources/commerce-modules/payment/payment-session#data-property).
|
||||||
*/
|
*/
|
||||||
data?: Record<string, unknown>
|
data?: Record<string, unknown>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Additional context that's useful for the payment provider to process the payment session.
|
* Additional context that's useful for the payment provider to process the payment session.
|
||||||
|
* Currently all of the context is calculated within the workflow.
|
||||||
*/
|
*/
|
||||||
context?: PaymentProviderContext
|
context?: Record<string, unknown>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createPaymentSessionsWorkflowId = "create-payment-sessions"
|
export const createPaymentSessionsWorkflowId = "create-payment-sessions"
|
||||||
/**
|
/**
|
||||||
* This workflow creates payment sessions. It's used by the
|
* This workflow creates payment sessions. It's used by the
|
||||||
* [Initialize Payment Session Store API Route](https://docs.medusajs.com/api/store#payment-collections_postpaymentcollectionsidpaymentsessions).
|
* [Initialize Payment Session Store API Route](https://docs.medusajs.com/api/store#payment-collections_postpaymentcollectionsidpaymentsessions).
|
||||||
*
|
*
|
||||||
* You can use this workflow within your own customizations or custom workflows, allowing you
|
* You can use this workflow within your own customizations or custom workflows, allowing you
|
||||||
* to create payment sessions in your custom flows.
|
* to create payment sessions in your custom flows.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const { result } = await createPaymentSessionsWorkflow(container)
|
* const { result } = await createPaymentSessionsWorkflow(container)
|
||||||
* .run({
|
* .run({
|
||||||
@@ -53,9 +65,9 @@ export const createPaymentSessionsWorkflowId = "create-payment-sessions"
|
|||||||
* provider_id: "pp_system"
|
* provider_id: "pp_system"
|
||||||
* }
|
* }
|
||||||
* })
|
* })
|
||||||
*
|
*
|
||||||
* @summary
|
* @summary
|
||||||
*
|
*
|
||||||
* Create payment sessions.
|
* Create payment sessions.
|
||||||
*/
|
*/
|
||||||
export const createPaymentSessionsWorkflow = createWorkflow(
|
export const createPaymentSessionsWorkflow = createWorkflow(
|
||||||
@@ -68,16 +80,89 @@ export const createPaymentSessionsWorkflow = createWorkflow(
|
|||||||
fields: ["id", "amount", "currency_code", "payment_sessions.*"],
|
fields: ["id", "amount", "currency_code", "payment_sessions.*"],
|
||||||
variables: { id: input.payment_collection_id },
|
variables: { id: input.payment_collection_id },
|
||||||
list: false,
|
list: false,
|
||||||
|
}).config({ name: "get-payment-collection" })
|
||||||
|
|
||||||
|
const { paymentCustomer, accountHolder } = when(
|
||||||
|
"customer-id-exists",
|
||||||
|
{ input },
|
||||||
|
(data) => {
|
||||||
|
return !!data.input.customer_id
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
const customer: CustomerDTO & { account_holder: AccountHolderDTO } =
|
||||||
|
useRemoteQueryStep({
|
||||||
|
entry_point: "customer",
|
||||||
|
fields: [
|
||||||
|
"id",
|
||||||
|
"email",
|
||||||
|
"company_name",
|
||||||
|
"first_name",
|
||||||
|
"last_name",
|
||||||
|
"phone",
|
||||||
|
"addresses.*",
|
||||||
|
"account_holder.*",
|
||||||
|
"metadata",
|
||||||
|
],
|
||||||
|
variables: { id: input.customer_id },
|
||||||
|
list: false,
|
||||||
|
}).config({ name: "get-customer" })
|
||||||
|
|
||||||
|
const paymentCustomer = transform({ customer }, (data) => {
|
||||||
|
return {
|
||||||
|
...data.customer,
|
||||||
|
billing_address:
|
||||||
|
data.customer.addresses?.find((a) => a.is_default_billing) ??
|
||||||
|
data.customer.addresses?.[0],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const accountHolderInput = {
|
||||||
|
provider_id: input.provider_id,
|
||||||
|
context: {
|
||||||
|
// The module is idempotent, so if there already is a linked account holder, the module will simply return it back.
|
||||||
|
account_holder: customer.account_holder,
|
||||||
|
customer: paymentCustomer,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountHolder = createPaymentAccountHolderStep(accountHolderInput)
|
||||||
|
return { paymentCustomer, accountHolder }
|
||||||
|
})
|
||||||
|
|
||||||
|
when(
|
||||||
|
"account-holder-created",
|
||||||
|
{ paymentCustomer, accountHolder },
|
||||||
|
(data) => {
|
||||||
|
return (
|
||||||
|
!isPresent(data.paymentCustomer?.account_holder) &&
|
||||||
|
isPresent(data.accountHolder)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
createRemoteLinkStep([
|
||||||
|
{
|
||||||
|
[Modules.CUSTOMER]: {
|
||||||
|
customer_id: paymentCustomer.id,
|
||||||
|
},
|
||||||
|
[Modules.PAYMENT]: {
|
||||||
|
account_holder_id: accountHolder.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
const paymentSessionInput = transform(
|
const paymentSessionInput = transform(
|
||||||
{ paymentCollection, input },
|
{ paymentCollection, paymentCustomer, accountHolder, input },
|
||||||
(data) => {
|
(data) => {
|
||||||
return {
|
return {
|
||||||
payment_collection_id: data.input.payment_collection_id,
|
payment_collection_id: data.input.payment_collection_id,
|
||||||
provider_id: data.input.provider_id,
|
provider_id: data.input.provider_id,
|
||||||
data: data.input.data,
|
data: data.input.data,
|
||||||
context: data.input.context,
|
context: {
|
||||||
|
...data.input.context,
|
||||||
|
customer: data.paymentCustomer,
|
||||||
|
account_holder: data.accountHolder,
|
||||||
|
},
|
||||||
amount: data.paymentCollection.amount,
|
amount: data.paymentCollection.amount,
|
||||||
currency_code: data.paymentCollection.currency_code,
|
currency_code: data.paymentCollection.currency_code,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,13 +23,13 @@ export type AuthorizePaymentSessionStepInput = {
|
|||||||
* The context to authorize the payment session with.
|
* The context to authorize the payment session with.
|
||||||
* This context is passed to the payment provider associated with the payment session.
|
* This context is passed to the payment provider associated with the payment session.
|
||||||
*/
|
*/
|
||||||
context: Record<string, unknown>
|
context?: Record<string, unknown>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const authorizePaymentSessionStepId = "authorize-payment-session-step"
|
export const authorizePaymentSessionStepId = "authorize-payment-session-step"
|
||||||
/**
|
/**
|
||||||
* This step authorizes a payment session.
|
* This step authorizes a payment session.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const data = authorizePaymentSessionStep({
|
* const data = authorizePaymentSessionStep({
|
||||||
* id: "payses_123",
|
* id: "payses_123",
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ export type CapturePaymentWorkflowInput = {
|
|||||||
|
|
||||||
export const capturePaymentWorkflowId = "capture-payment-workflow"
|
export const capturePaymentWorkflowId = "capture-payment-workflow"
|
||||||
/**
|
/**
|
||||||
* This workflow captures a payment. It's used by the
|
* This workflow captures a payment. It's used by the
|
||||||
* [Capture Payment Admin API Route](https://docs.medusajs.com/api/admin#payments_postpaymentsidcapture).
|
* [Capture Payment Admin API Route](https://docs.medusajs.com/api/admin#payments_postpaymentsidcapture).
|
||||||
*
|
*
|
||||||
* You can use this workflow within your own customizations or custom workflows, allowing you
|
* You can use this workflow within your own customizations or custom workflows, allowing you
|
||||||
* to capture a payment in your custom flows.
|
* to capture a payment in your custom flows.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const { result } = await capturePaymentWorkflow(container)
|
* const { result } = await capturePaymentWorkflow(container)
|
||||||
* .run({
|
* .run({
|
||||||
@@ -44,9 +44,9 @@ export const capturePaymentWorkflowId = "capture-payment-workflow"
|
|||||||
* payment_id: "pay_123"
|
* payment_id: "pay_123"
|
||||||
* }
|
* }
|
||||||
* })
|
* })
|
||||||
*
|
*
|
||||||
* @summary
|
* @summary
|
||||||
*
|
*
|
||||||
* Capture a payment.
|
* Capture a payment.
|
||||||
*/
|
*/
|
||||||
export const capturePaymentWorkflow = createWorkflow(
|
export const capturePaymentWorkflow = createWorkflow(
|
||||||
|
|||||||
@@ -8,15 +8,10 @@ export interface StoreInitializePaymentSession {
|
|||||||
* for.
|
* for.
|
||||||
*/
|
*/
|
||||||
provider_id: string
|
provider_id: string
|
||||||
/**
|
|
||||||
* The payment's context, such as the customer or address details. if the customer is logged-in,
|
|
||||||
* the customer id is set in the context under a `customer.id` property.
|
|
||||||
*/
|
|
||||||
context?: Record<string, unknown>
|
|
||||||
/**
|
/**
|
||||||
* Any data necessary for the payment provider to process the payment.
|
* Any data necessary for the payment provider to process the payment.
|
||||||
*
|
*
|
||||||
* Learn more in [this documentation](https://docs.medusajs.com/resources/commerce-modules/payment/payment-session#data-property).
|
* Learn more in [this documentation](https://docs.medusajs.com/resources/commerce-modules/payment/payment-session#data-property).
|
||||||
*/
|
*/
|
||||||
data?: Record<string, unknown>
|
data?: Record<string, unknown>
|
||||||
}
|
}
|
||||||
@@ -662,3 +662,45 @@ export interface PaymentMethodDTO {
|
|||||||
*/
|
*/
|
||||||
provider_id: string
|
provider_id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AccountHolderDTO {
|
||||||
|
/**
|
||||||
|
* The ID of the account holder.
|
||||||
|
*/
|
||||||
|
id: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID of the associated payment provider.
|
||||||
|
*/
|
||||||
|
provider_id: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The external ID of the account holder in the payment provider system.
|
||||||
|
*/
|
||||||
|
external_id: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The email of the account holder.
|
||||||
|
*/
|
||||||
|
email: string | null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data of the account holder, as returned by the payment provider.
|
||||||
|
*/
|
||||||
|
data: Record<string, unknown>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the account holder was created.
|
||||||
|
*/
|
||||||
|
created_at?: string | Date | null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the account holder was updated.
|
||||||
|
*/
|
||||||
|
updated_at?: string | Date | null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds custom data in key-value pairs.
|
||||||
|
*/
|
||||||
|
metadata?: Record<string, unknown> | null
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { BigNumberInput } from "../totals"
|
import { BigNumberInput } from "../totals"
|
||||||
import { PaymentCollectionStatus } from "./common"
|
import { PaymentCollectionStatus } from "./common"
|
||||||
import { PaymentProviderContext } from "./provider"
|
import { PaymentCustomerDTO, PaymentProviderContext } from "./provider"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The payment collection to be created.
|
* The payment collection to be created.
|
||||||
@@ -255,6 +255,26 @@ export interface CreatePaymentProviderDTO {
|
|||||||
is_enabled?: boolean
|
is_enabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The payment session to be created.
|
||||||
|
*/
|
||||||
|
export interface CreateAccountHolderDTO {
|
||||||
|
/**
|
||||||
|
* The provider's ID.
|
||||||
|
*/
|
||||||
|
provider_id: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Necessary context data for the associated payment provider.
|
||||||
|
*/
|
||||||
|
context: PaymentProviderContext & {
|
||||||
|
/**
|
||||||
|
* The customer information from Medusa.
|
||||||
|
*/
|
||||||
|
customer: PaymentCustomerDTO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The details of the webhook event payload.
|
* The details of the webhook event payload.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { AddressDTO } from "../address"
|
import { AddressDTO } from "../address"
|
||||||
import { CustomerDTO } from "../customer"
|
|
||||||
import { BigNumberInput, BigNumberValue } from "../totals"
|
import { BigNumberInput, BigNumberValue } from "../totals"
|
||||||
import { PaymentSessionStatus } from "./common"
|
import { AccountHolderDTO, PaymentSessionStatus } from "./common"
|
||||||
import { ProviderWebhookPayload } from "./mutations"
|
import { ProviderWebhookPayload } from "./mutations"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,7 +11,19 @@ export type PaymentAddressDTO = Partial<AddressDTO>
|
|||||||
/**
|
/**
|
||||||
* The customer associated with the payment.
|
* The customer associated with the payment.
|
||||||
*/
|
*/
|
||||||
export type PaymentCustomerDTO = Partial<CustomerDTO>
|
export type PaymentCustomerDTO = {
|
||||||
|
id: string
|
||||||
|
email: string
|
||||||
|
company_name?: string | null
|
||||||
|
first_name?: string | null
|
||||||
|
last_name?: string | null
|
||||||
|
phone?: string | null
|
||||||
|
billing_address?: PaymentAddressDTO | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PaymentAccountHolderDTO = {
|
||||||
|
data: Record<string, unknown>
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalized events from payment provider to internal payment module events.
|
* Normalized events from payment provider to internal payment module events.
|
||||||
@@ -26,33 +37,26 @@ export type PaymentActions =
|
|||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* Context data provided to the payment provider when authorizing a payment session.
|
* Context data provided to the payment provider
|
||||||
*/
|
*/
|
||||||
export type PaymentProviderContext = {
|
export type PaymentProviderContext = {
|
||||||
/**
|
/**
|
||||||
* The payment's billing address.
|
* The account holder information, if available for the payment provider.
|
||||||
*/
|
*/
|
||||||
billing_address?: PaymentAddressDTO
|
account_holder?: PaymentAccountHolderDTO
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The associated customer's email.
|
* The customer information from Medusa.
|
||||||
*/
|
|
||||||
email?: string
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The ID of payment session the provider payment is associated with.
|
|
||||||
*/
|
|
||||||
session_id?: string
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The customer associated with this payment.
|
|
||||||
*/
|
*/
|
||||||
customer?: PaymentCustomerDTO
|
customer?: PaymentCustomerDTO
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
export type PaymentProviderInput = {
|
||||||
* The extra fields specific to the provider session.
|
// Data is a combination of the input from the user and whatever is stored in the DB for this entity.
|
||||||
*/
|
data?: Record<string, unknown>
|
||||||
extra?: Record<string, unknown>
|
|
||||||
|
// The context for this payment operation. The data is guaranteed to be validated and not directly provided by the user.
|
||||||
|
context?: PaymentProviderContext
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -61,12 +65,7 @@ export type PaymentProviderContext = {
|
|||||||
* The data used initiate a payment in a provider when a payment
|
* The data used initiate a payment in a provider when a payment
|
||||||
* session is created.
|
* session is created.
|
||||||
*/
|
*/
|
||||||
export type CreatePaymentProviderSession = {
|
export type InitiatePaymentInput = PaymentProviderInput & {
|
||||||
/**
|
|
||||||
* A context necessary for the payment provider.
|
|
||||||
*/
|
|
||||||
context: PaymentProviderContext
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The amount to be authorized.
|
* The amount to be authorized.
|
||||||
*/
|
*/
|
||||||
@@ -78,34 +77,12 @@ export type CreatePaymentProviderSession = {
|
|||||||
currency_code: string
|
currency_code: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SavePaymentMethod = {
|
|
||||||
/**
|
|
||||||
* Any data that should be used by the provider for saving the payment method.
|
|
||||||
*/
|
|
||||||
data: Record<string, unknown>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The context of the payment provider, such as the customer ID.
|
|
||||||
*/
|
|
||||||
context: PaymentProviderContext
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* The attributes to update a payment related to a payment session in a provider.
|
* The attributes to update a payment related to a payment session in a provider.
|
||||||
*/
|
*/
|
||||||
export type UpdatePaymentProviderSession = {
|
export type UpdatePaymentInput = PaymentProviderInput & {
|
||||||
/**
|
|
||||||
* A payment's context.
|
|
||||||
*/
|
|
||||||
context: PaymentProviderContext
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The `data` field of the payment session.
|
|
||||||
*/
|
|
||||||
data: Record<string, unknown>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The payment session's amount.
|
* The payment session's amount.
|
||||||
*/
|
*/
|
||||||
@@ -117,29 +94,58 @@ export type UpdatePaymentProviderSession = {
|
|||||||
currency_code: string
|
currency_code: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type DeletePaymentInput = PaymentProviderInput
|
||||||
|
|
||||||
|
export type AuthorizePaymentInput = PaymentProviderInput
|
||||||
|
|
||||||
|
export type CapturePaymentInput = PaymentProviderInput
|
||||||
|
|
||||||
|
export type RefundPaymentInput = PaymentProviderInput & {
|
||||||
|
/**
|
||||||
|
* The amount to refund.
|
||||||
|
*/
|
||||||
|
amount: BigNumberInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RetrievePaymentInput = PaymentProviderInput
|
||||||
|
|
||||||
|
export type CancelPaymentInput = PaymentProviderInput
|
||||||
|
|
||||||
|
export type CreateAccountHolderInput = PaymentProviderInput & {
|
||||||
|
context: Omit<PaymentProviderContext, "customer"> & {
|
||||||
|
customer: PaymentCustomerDTO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DeleteAccountHolderInput = PaymentProviderInput & {
|
||||||
|
context: Omit<PaymentProviderContext, "account_holder"> & {
|
||||||
|
account_holder: Partial<AccountHolderDTO>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ListPaymentMethodsInput = PaymentProviderInput
|
||||||
|
|
||||||
|
export type SavePaymentMethodInput = PaymentProviderInput
|
||||||
|
|
||||||
|
export type GetPaymentStatusInput = PaymentProviderInput
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* The response of operations on a payment.
|
* The response of operations on a payment.
|
||||||
*/
|
*/
|
||||||
export type PaymentProviderSessionResponse = {
|
export type PaymentProviderOutput = {
|
||||||
/**
|
/**
|
||||||
* The data to be stored in the `data` field of the Payment Session to be created.
|
* The unstrucvtured data returned from the payment provider. The content will vary between providers.
|
||||||
* 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>
|
data?: Record<string, unknown>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SavePaymentMethodResponse = {
|
export type InitiatePaymentOutput = PaymentProviderOutput & {
|
||||||
/**
|
/**
|
||||||
* The ID of the payment method in the payment provider.
|
* The ID of the payment session in the payment provider.
|
||||||
*/
|
*/
|
||||||
id: string
|
id: string
|
||||||
|
|
||||||
/**
|
|
||||||
* The data returned from the payment provider after saving the payment method.
|
|
||||||
*/
|
|
||||||
data: Record<string, unknown>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -147,59 +153,53 @@ export type SavePaymentMethodResponse = {
|
|||||||
*
|
*
|
||||||
* The successful result of authorizing a payment session using a payment provider.
|
* The successful result of authorizing a payment session using a payment provider.
|
||||||
*/
|
*/
|
||||||
export type PaymentProviderAuthorizeResponse = {
|
export type AuthorizePaymentOutput = PaymentProviderOutput & {
|
||||||
/**
|
/**
|
||||||
* The status of the payment, which will be stored in the payment session's `status` field.
|
* The status of the payment, which will be stored in the payment session's `status` field.
|
||||||
*/
|
*/
|
||||||
status: PaymentSessionStatus
|
status: PaymentSessionStatus
|
||||||
|
|
||||||
/**
|
|
||||||
* The `data` to be stored in the payment session's `data` field.
|
|
||||||
*/
|
|
||||||
data: PaymentProviderSessionResponse["data"]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PaymentMethodResponse = {
|
export type UpdatePaymentOutput = PaymentProviderOutput
|
||||||
|
|
||||||
|
export type DeletePaymentOutput = PaymentProviderOutput
|
||||||
|
|
||||||
|
export type CapturePaymentOutput = PaymentProviderOutput
|
||||||
|
|
||||||
|
export type RefundPaymentOutput = PaymentProviderOutput
|
||||||
|
|
||||||
|
export type RetrievePaymentOutput = PaymentProviderOutput
|
||||||
|
|
||||||
|
export type CancelPaymentOutput = PaymentProviderOutput
|
||||||
|
|
||||||
|
export type CreateAccountHolderOutput = PaymentProviderOutput & {
|
||||||
|
/**
|
||||||
|
* The ID of the account holder in the payment provider.
|
||||||
|
*/
|
||||||
id: string
|
id: string
|
||||||
data: Record<string, unknown>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export type DeleteAccountHolderOutput = PaymentProviderOutput
|
||||||
* @interface
|
|
||||||
*
|
|
||||||
* The details of which payment provider to use to perform an action, and what
|
|
||||||
* data to be passed to that provider.
|
|
||||||
*/
|
|
||||||
export type PaymentProviderDataInput = {
|
|
||||||
/**
|
|
||||||
* The ID of the provider to be used to perform an action.
|
|
||||||
*/
|
|
||||||
provider_id: string
|
|
||||||
|
|
||||||
|
export type ListPaymentMethodsOutput = (PaymentProviderOutput & {
|
||||||
/**
|
/**
|
||||||
* The data to be passed to the provider.
|
* The ID of the payment method in the payment provider.
|
||||||
*/
|
*/
|
||||||
data: Record<string, unknown>
|
id: string
|
||||||
|
})[]
|
||||||
|
|
||||||
|
export type SavePaymentMethodOutput = PaymentProviderOutput & {
|
||||||
|
/**
|
||||||
|
* The ID of the payment method in the payment provider.
|
||||||
|
*/
|
||||||
|
id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export type GetPaymentStatusOutput = PaymentProviderOutput & {
|
||||||
* An object that is returned in case of an error.
|
|
||||||
*/
|
|
||||||
export interface PaymentProviderError {
|
|
||||||
/**
|
/**
|
||||||
* The error message
|
* The status of the payment, which will be stored in the payment session's `status` field.
|
||||||
*/
|
*/
|
||||||
error: string
|
status: PaymentSessionStatus
|
||||||
|
|
||||||
/**
|
|
||||||
* The error code.
|
|
||||||
*/
|
|
||||||
code?: string
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Any additional helpful details.
|
|
||||||
*/
|
|
||||||
detail?: any
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -244,51 +244,39 @@ export interface IPaymentProvider {
|
|||||||
*/
|
*/
|
||||||
getIdentifier(): string
|
getIdentifier(): string
|
||||||
|
|
||||||
initiatePayment(
|
initiatePayment(data: InitiatePaymentInput): Promise<InitiatePaymentOutput>
|
||||||
data: CreatePaymentProviderSession
|
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse>
|
|
||||||
|
|
||||||
updatePayment(
|
updatePayment(data: UpdatePaymentInput): Promise<UpdatePaymentOutput>
|
||||||
context: UpdatePaymentProviderSession
|
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse>
|
|
||||||
|
|
||||||
deletePayment(
|
deletePayment(data: DeletePaymentInput): Promise<DeletePaymentOutput>
|
||||||
paymentSessionData: Record<string, unknown>
|
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
|
||||||
|
|
||||||
authorizePayment(
|
authorizePayment(data: AuthorizePaymentInput): Promise<AuthorizePaymentOutput>
|
||||||
paymentSessionData: Record<string, unknown>,
|
|
||||||
context: Record<string, unknown>
|
|
||||||
): Promise<PaymentProviderError | PaymentProviderAuthorizeResponse>
|
|
||||||
|
|
||||||
capturePayment(
|
capturePayment(data: CapturePaymentInput): Promise<CapturePaymentOutput>
|
||||||
paymentSessionData: Record<string, unknown>
|
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
|
||||||
|
|
||||||
refundPayment(
|
refundPayment(data: RefundPaymentInput): Promise<RefundPaymentOutput>
|
||||||
paymentSessionData: Record<string, unknown>,
|
|
||||||
refundAmount: BigNumberInput
|
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
|
||||||
|
|
||||||
retrievePayment(
|
retrievePayment(data: RetrievePaymentInput): Promise<RetrievePaymentOutput>
|
||||||
paymentSessionData: Record<string, unknown>
|
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
|
||||||
|
|
||||||
cancelPayment(
|
cancelPayment(data: CancelPaymentInput): Promise<CancelPaymentOutput>
|
||||||
paymentSessionData: Record<string, unknown>
|
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
createAccountHolder?(
|
||||||
|
data: CreateAccountHolderInput
|
||||||
|
): Promise<CreateAccountHolderOutput>
|
||||||
|
|
||||||
|
deleteAccountHolder?(
|
||||||
|
data: DeleteAccountHolderInput
|
||||||
|
): Promise<DeleteAccountHolderOutput>
|
||||||
|
|
||||||
listPaymentMethods?(
|
listPaymentMethods?(
|
||||||
context: PaymentProviderContext
|
data: ListPaymentMethodsInput
|
||||||
): Promise<PaymentMethodResponse[]>
|
): Promise<ListPaymentMethodsOutput>
|
||||||
|
|
||||||
savePaymentMethod?(
|
savePaymentMethod?(
|
||||||
input: SavePaymentMethod
|
data: SavePaymentMethodInput
|
||||||
): Promise<PaymentProviderError | SavePaymentMethodResponse>
|
): Promise<SavePaymentMethodOutput>
|
||||||
|
|
||||||
getPaymentStatus(
|
getPaymentStatus(data: GetPaymentStatusInput): Promise<GetPaymentStatusOutput>
|
||||||
paymentSessionData: Record<string, unknown>
|
|
||||||
): Promise<PaymentSessionStatus>
|
|
||||||
|
|
||||||
getWebhookActionAndData(
|
getWebhookActionAndData(
|
||||||
data: ProviderWebhookPayload["payload"]
|
data: ProviderWebhookPayload["payload"]
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { RestoreReturn, SoftDeleteReturn } from "../dal"
|
|||||||
import { IModuleService } from "../modules-sdk"
|
import { IModuleService } from "../modules-sdk"
|
||||||
import { Context } from "../shared-context"
|
import { Context } from "../shared-context"
|
||||||
import {
|
import {
|
||||||
|
AccountHolderDTO,
|
||||||
CaptureDTO,
|
CaptureDTO,
|
||||||
FilterableCaptureProps,
|
FilterableCaptureProps,
|
||||||
FilterablePaymentCollectionProps,
|
FilterablePaymentCollectionProps,
|
||||||
@@ -31,6 +32,7 @@ import {
|
|||||||
UpdatePaymentDTO,
|
UpdatePaymentDTO,
|
||||||
UpdatePaymentSessionDTO,
|
UpdatePaymentSessionDTO,
|
||||||
UpdateRefundReasonDTO,
|
UpdateRefundReasonDTO,
|
||||||
|
CreateAccountHolderDTO,
|
||||||
UpsertPaymentCollectionDTO,
|
UpsertPaymentCollectionDTO,
|
||||||
} from "./mutations"
|
} from "./mutations"
|
||||||
import { WebhookActionResult } from "./provider"
|
import { WebhookActionResult } from "./provider"
|
||||||
@@ -751,6 +753,63 @@ export interface IPaymentModuleService extends IModuleService {
|
|||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<[PaymentProviderDTO[], number]>
|
): Promise<[PaymentProviderDTO[], number]>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method creates(if supported by provider) the account holder in the payment provider.
|
||||||
|
*
|
||||||
|
* @param {CreateAccountHolderDTO} data - The details of the account holder.
|
||||||
|
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||||
|
* @returns {Promise<Record<string, unknown>>} The account holder's details in the payment provider, typically just the ID.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const accountHolder =
|
||||||
|
* await paymentModuleService.createAccountHolder(
|
||||||
|
* {
|
||||||
|
* provider_id: "stripe",
|
||||||
|
* context: {
|
||||||
|
* customer: {
|
||||||
|
* id: "cus_123",
|
||||||
|
* },
|
||||||
|
* },
|
||||||
|
* }
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* remoteLink.create([{
|
||||||
|
* [Modules.CUSTOMER]: {
|
||||||
|
* customer_id: "cus_123",
|
||||||
|
* },
|
||||||
|
* [Modules.PAYMENT]: {
|
||||||
|
* account_holder_id: accountHolder.id,
|
||||||
|
* },
|
||||||
|
* }])
|
||||||
|
*/
|
||||||
|
createAccountHolder(
|
||||||
|
input: CreateAccountHolderDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<AccountHolderDTO>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method deletes the account holder in the payment provider.
|
||||||
|
*
|
||||||
|
* @param {string} id - The account holder's ID.
|
||||||
|
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||||
|
* @returns {Promise<void>} Resolves when the account holder is deleted successfully.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* await paymentModuleService.deleteAccountHolder({
|
||||||
|
* id: "acc_holder_123",
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* remoteLink.dismiss([{
|
||||||
|
* [Modules.CUSTOMER]: {
|
||||||
|
* customer_id: "cus_123",
|
||||||
|
* },
|
||||||
|
* [Modules.PAYMENT]: {
|
||||||
|
* account_holder_id: "acc_holder_123",
|
||||||
|
* },
|
||||||
|
* }])
|
||||||
|
*/
|
||||||
|
deleteAccountHolder(id: string, sharedContext?: Context): Promise<void>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method retrieves all payment methods based on the context and configuration.
|
* This method retrieves all payment methods based on the context and configuration.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -122,4 +122,10 @@ export const LINKS = {
|
|||||||
Modules.FULFILLMENT,
|
Modules.FULFILLMENT,
|
||||||
"shipping_profile_id"
|
"shipping_profile_id"
|
||||||
),
|
),
|
||||||
|
CustomerAccountHolder: composeLinkName(
|
||||||
|
Modules.CUSTOMER,
|
||||||
|
"customer_id",
|
||||||
|
Modules.PAYMENT,
|
||||||
|
"account_holder_id"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,25 @@
|
|||||||
import {
|
import {
|
||||||
CreatePaymentProviderSession,
|
|
||||||
IPaymentProvider,
|
IPaymentProvider,
|
||||||
PaymentProviderError,
|
|
||||||
PaymentProviderSessionResponse,
|
|
||||||
PaymentSessionStatus,
|
|
||||||
ProviderWebhookPayload,
|
ProviderWebhookPayload,
|
||||||
UpdatePaymentProviderSession,
|
|
||||||
WebhookActionResult,
|
WebhookActionResult,
|
||||||
|
CapturePaymentInput,
|
||||||
|
CapturePaymentOutput,
|
||||||
|
AuthorizePaymentInput,
|
||||||
|
AuthorizePaymentOutput,
|
||||||
|
CancelPaymentInput,
|
||||||
|
CancelPaymentOutput,
|
||||||
|
InitiatePaymentInput,
|
||||||
|
InitiatePaymentOutput,
|
||||||
|
DeletePaymentInput,
|
||||||
|
DeletePaymentOutput,
|
||||||
|
GetPaymentStatusInput,
|
||||||
|
GetPaymentStatusOutput,
|
||||||
|
RefundPaymentInput,
|
||||||
|
RefundPaymentOutput,
|
||||||
|
RetrievePaymentInput,
|
||||||
|
RetrievePaymentOutput,
|
||||||
|
UpdatePaymentInput,
|
||||||
|
UpdatePaymentOutput,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
|
|
||||||
export abstract class AbstractPaymentProvider<TConfig = Record<string, unknown>>
|
export abstract class AbstractPaymentProvider<TConfig = Record<string, unknown>>
|
||||||
@@ -147,48 +160,34 @@ export abstract class AbstractPaymentProvider<TConfig = Record<string, unknown>>
|
|||||||
*
|
*
|
||||||
* In this method, use the third-party provider to capture the payment.
|
* In this method, use the third-party provider to capture the payment.
|
||||||
*
|
*
|
||||||
* @param paymentData - The `data` property of the payment. Make sure to store in it
|
* @param input - The input to capture the payment. The `data` field should contain the data from the payment provider. when the payment was created.
|
||||||
* any helpful identification for your third-party integration.
|
* @returns The new data to store in the payment's `data` property. Throws in case of an error.
|
||||||
* @returns The new data to store in the payment's `data` property, or an error object.
|
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* // other imports...
|
* // other imports...
|
||||||
* import {
|
* import {
|
||||||
* PaymentProviderError,
|
* CapturePaymentInput,
|
||||||
* PaymentProviderSessionResponse,
|
* CapturePaymentOutput,
|
||||||
* } from "@medusajs/framework/types"
|
* } from "@medusajs/framework/types"
|
||||||
*
|
*
|
||||||
* class MyPaymentProviderService extends AbstractPaymentProvider<
|
* class MyPaymentProviderService extends AbstractPaymentProvider<
|
||||||
* Options
|
* Options
|
||||||
* > {
|
* > {
|
||||||
* async capturePayment(
|
* async capturePayment(
|
||||||
* paymentData: Record<string, unknown>
|
* input: CapturePaymentInput
|
||||||
* ): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]> {
|
* ): Promise<CapturePaymentOutput> {
|
||||||
* const externalId = paymentData.id
|
* const externalId = input.data?.id
|
||||||
*
|
*
|
||||||
* try {
|
|
||||||
* // assuming you have a client that captures the payment
|
* // assuming you have a client that captures the payment
|
||||||
* const newData = await this.client.capturePayment(externalId)
|
* const newData = await this.client.capturePayment(externalId)
|
||||||
*
|
* return {data: newData}
|
||||||
* return {
|
|
||||||
* ...newData,
|
|
||||||
* id: externalId
|
|
||||||
* }
|
|
||||||
* } catch (e) {
|
|
||||||
* return {
|
|
||||||
* error: e,
|
|
||||||
* code: "unknown",
|
|
||||||
* detail: e
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
* }
|
||||||
*
|
|
||||||
* // ...
|
* // ...
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
abstract capturePayment(
|
abstract capturePayment(
|
||||||
paymentData: Record<string, unknown>
|
input: CapturePaymentInput
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
): Promise<CapturePaymentOutput>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method authorizes a payment session. When authorized successfully, a payment is created by the Payment
|
* This method authorizes a payment session. When authorized successfully, a payment is created by the Payment
|
||||||
@@ -199,18 +198,14 @@ export abstract class AbstractPaymentProvider<TConfig = Record<string, unknown>>
|
|||||||
*
|
*
|
||||||
* To automatically capture the payment after authorization, return the status `captured`.
|
* To automatically capture the payment after authorization, return the status `captured`.
|
||||||
*
|
*
|
||||||
* @param paymentSessionData - The `data` property of the payment session. Make sure to store in it
|
* @param input - The input to authorize the payment. The `data` field should contain the data from the payment provider. when the payment was created.
|
||||||
* any helpful identification for your third-party integration.
|
* @returns The status of the authorization, along with the `data` field about the payment. Throws in case of an error.
|
||||||
* @param context - The context in which the payment is being authorized. For example, in checkout,
|
|
||||||
* the context has a `cart_id` property indicating the ID of the associated cart.
|
|
||||||
* @returns Either an object of the new data to store in the created payment's `data` property and the
|
|
||||||
* payment's status, or an error object. Make sure to set in `data` anything useful to later retrieve the session.
|
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* // other imports...
|
* // other imports...
|
||||||
* import {
|
* import {
|
||||||
* PaymentProviderError,
|
* AuthorizePaymentInput,
|
||||||
* PaymentProviderSessionResponse,
|
* AuthorizePaymentOutput,
|
||||||
* PaymentSessionStatus
|
* PaymentSessionStatus
|
||||||
* } from "@medusajs/framework/types"
|
* } from "@medusajs/framework/types"
|
||||||
*
|
*
|
||||||
@@ -219,33 +214,16 @@ export abstract class AbstractPaymentProvider<TConfig = Record<string, unknown>>
|
|||||||
* Options
|
* Options
|
||||||
* > {
|
* > {
|
||||||
* async authorizePayment(
|
* async authorizePayment(
|
||||||
* paymentSessionData: Record<string, unknown>,
|
* input: AuthorizePaymentInput
|
||||||
* context: Record<string, unknown>
|
* ): Promise<AuthorizePaymentOutput> {
|
||||||
* ): Promise<
|
* const externalId = input.data?.id
|
||||||
* PaymentProviderError | {
|
|
||||||
* status: PaymentSessionStatus
|
|
||||||
* data: PaymentProviderSessionResponse["data"]
|
|
||||||
* }
|
|
||||||
* > {
|
|
||||||
* const externalId = paymentSessionData.id
|
|
||||||
*
|
*
|
||||||
* try {
|
* // assuming you have a client that authorizes the payment
|
||||||
* // assuming you have a client that authorizes the payment
|
* const paymentData = await this.client.authorizePayment(externalId)
|
||||||
* const paymentData = await this.client.authorizePayment(externalId)
|
|
||||||
*
|
*
|
||||||
* return {
|
* return {
|
||||||
* data: {
|
* data: paymentData,
|
||||||
* ...paymentData,
|
* status: "authorized"
|
||||||
* id: externalId
|
|
||||||
* },
|
|
||||||
* status: "authorized"
|
|
||||||
* }
|
|
||||||
* } catch (e) {
|
|
||||||
* return {
|
|
||||||
* error: e,
|
|
||||||
* code: "unknown",
|
|
||||||
* detail: e
|
|
||||||
* }
|
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
@@ -253,28 +231,14 @@ export abstract class AbstractPaymentProvider<TConfig = Record<string, unknown>>
|
|||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
abstract authorizePayment(
|
abstract authorizePayment(
|
||||||
paymentSessionData: Record<string, unknown>,
|
input: AuthorizePaymentInput
|
||||||
context: Record<string, unknown>
|
): Promise<AuthorizePaymentOutput>
|
||||||
): Promise<
|
|
||||||
| PaymentProviderError
|
|
||||||
| {
|
|
||||||
/**
|
|
||||||
* The new status of the payment.
|
|
||||||
*/
|
|
||||||
status: PaymentSessionStatus
|
|
||||||
/**
|
|
||||||
* The data to store in the created payment's `data` property.
|
|
||||||
*/
|
|
||||||
data: PaymentProviderSessionResponse["data"]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method cancels a payment.
|
* This method cancels a payment.
|
||||||
*
|
*
|
||||||
* @param paymentData - The `data` property of the payment. Make sure to store in it
|
* @param input - The input to cancel the payment. The `data` field should contain the data from the payment provider. when the payment was created.
|
||||||
* any helpful identification for your third-party integration.
|
* @returns The new data to store in the payment's `data` property, if any. Throws in case of an error.
|
||||||
* @returns An error object if an error occurs, or the data received from the integration.
|
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* // other imports...
|
* // other imports...
|
||||||
@@ -288,42 +252,34 @@ export abstract class AbstractPaymentProvider<TConfig = Record<string, unknown>>
|
|||||||
* Options
|
* Options
|
||||||
* > {
|
* > {
|
||||||
* async cancelPayment(
|
* async cancelPayment(
|
||||||
* paymentData: Record<string, unknown>
|
* input: CancelPaymentInput
|
||||||
* ): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]> {
|
* ): Promise<CancelPaymentOutput> {
|
||||||
* const externalId = paymentData.id
|
* const externalId = input.data?.id
|
||||||
*
|
*
|
||||||
* try {
|
* // assuming you have a client that cancels the payment
|
||||||
* // assuming you have a client that cancels the payment
|
* const paymentData = await this.client.cancelPayment(externalId)
|
||||||
* const paymentData = await this.client.cancelPayment(externalId)
|
* return { data: paymentData }
|
||||||
* } catch (e) {
|
|
||||||
* return {
|
|
||||||
* error: e,
|
|
||||||
* code: "unknown",
|
|
||||||
* detail: e
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* // ...
|
* // ...
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
abstract cancelPayment(
|
abstract cancelPayment(
|
||||||
paymentData: Record<string, unknown>
|
input: CancelPaymentInput
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
): Promise<CancelPaymentOutput>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is used when a payment session is created. It can be used to initiate the payment
|
* This method is used when a payment session is created. It can be used to initiate the payment
|
||||||
* in the third-party session, before authorizing or capturing the payment later.
|
* in the third-party session, before authorizing or capturing the payment later.
|
||||||
*
|
*
|
||||||
* @param context - The details of the payment session and its context.
|
* @param input - The input to create the payment session.
|
||||||
* @returns An object whose `data` property is set in the created payment session, or an error
|
* @returns The new data to store in the payment's `data` property. Throws in case of an error.
|
||||||
* object. Make sure to set in `data` anything useful to later retrieve the session.
|
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* // other imports...
|
* // other imports...
|
||||||
* import {
|
* import {
|
||||||
* PaymentProviderError,
|
* InitiatePaymentInput,
|
||||||
* PaymentProviderSessionResponse,
|
* InitiatePaymentOutput,
|
||||||
* } from "@medusajs/framework/types"
|
* } from "@medusajs/framework/types"
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
@@ -331,32 +287,22 @@ export abstract class AbstractPaymentProvider<TConfig = Record<string, unknown>>
|
|||||||
* Options
|
* Options
|
||||||
* > {
|
* > {
|
||||||
* async initiatePayment(
|
* async initiatePayment(
|
||||||
* context: CreatePaymentProviderSession
|
* input: InitiatePaymentInput
|
||||||
* ): Promise<PaymentProviderError | PaymentProviderSessionResponse> {
|
* ): Promise<InitiatePaymentOutput> {
|
||||||
* const {
|
* const {
|
||||||
* amount,
|
* amount,
|
||||||
* currency_code,
|
* currency_code,
|
||||||
* context: customerDetails
|
* context: customerDetails
|
||||||
* } = context
|
* } = input
|
||||||
*
|
*
|
||||||
* try {
|
* // assuming you have a client that initializes the payment
|
||||||
* // assuming you have a client that initializes the payment
|
* const response = await this.client.init(
|
||||||
* const response = await this.client.init(
|
* amount, currency_code, customerDetails
|
||||||
* amount, currency_code, customerDetails
|
* )
|
||||||
* )
|
|
||||||
*
|
*
|
||||||
* return {
|
* return {
|
||||||
* ...response,
|
* id: response.id
|
||||||
* data: {
|
* data: response,
|
||||||
* id: response.id
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* } catch (e) {
|
|
||||||
* return {
|
|
||||||
* error: e,
|
|
||||||
* code: "unknown",
|
|
||||||
* detail: e
|
|
||||||
* }
|
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
@@ -364,23 +310,22 @@ export abstract class AbstractPaymentProvider<TConfig = Record<string, unknown>>
|
|||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
abstract initiatePayment(
|
abstract initiatePayment(
|
||||||
context: CreatePaymentProviderSession
|
input: InitiatePaymentInput
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse>
|
): Promise<InitiatePaymentOutput>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is used when a payment session is deleted, which can only happen if it isn't authorized, yet.
|
* This method is used when a payment session is deleted, which can only happen if it isn't authorized, yet.
|
||||||
*
|
*
|
||||||
* Use this to delete or cancel the payment in the third-party service.
|
* Use this to delete or cancel the payment in the third-party service.
|
||||||
*
|
*
|
||||||
* @param paymentSessionData - The `data` property of the payment session. Make sure to store in it
|
* @param input - The input to delete the payment session. The `data` field should contain the data from the payment provider. when the payment was created.
|
||||||
* any helpful identification for your third-party integration.
|
* @returns The new data to store in the payment's `data` property, if any. Throws in case of an error.
|
||||||
* @returns An error object or the response from the third-party service.
|
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* // other imports...
|
* // other imports...
|
||||||
* import {
|
* import {
|
||||||
* PaymentProviderError,
|
* DeletePaymentInput,
|
||||||
* PaymentProviderSessionResponse,
|
* DeletePaymentOutput,
|
||||||
* } from "@medusajs/framework/types"
|
* } from "@medusajs/framework/types"
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
@@ -388,41 +333,34 @@ export abstract class AbstractPaymentProvider<TConfig = Record<string, unknown>>
|
|||||||
* Options
|
* Options
|
||||||
* > {
|
* > {
|
||||||
* async deletePayment(
|
* async deletePayment(
|
||||||
* paymentSessionData: Record<string, unknown>
|
* input: DeletePaymentInput
|
||||||
* ): Promise<
|
* ): Promise<DeletePaymentOutput> {
|
||||||
* PaymentProviderError | PaymentProviderSessionResponse["data"]
|
* const externalId = input.data?.id
|
||||||
* > {
|
|
||||||
* const externalId = paymentSessionData.id
|
|
||||||
*
|
*
|
||||||
* try {
|
* // assuming you have a client that cancels the payment
|
||||||
* // assuming you have a client that cancels the payment
|
* await this.client.cancelPayment(externalId)
|
||||||
* await this.client.cancelPayment(externalId)
|
* return {}
|
||||||
* } catch (e) {
|
* }
|
||||||
* return {
|
|
||||||
* error: e,
|
|
||||||
* code: "unknown",
|
|
||||||
* detail: e
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* // ...
|
* // ...
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
abstract deletePayment(
|
abstract deletePayment(
|
||||||
paymentSessionData: Record<string, unknown>
|
input: DeletePaymentInput
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
): Promise<DeletePaymentOutput>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method gets the status of a payment session based on the status in the third-party integration.
|
* This method gets the status of a payment session based on the status in the third-party integration.
|
||||||
*
|
*
|
||||||
* @param paymentSessionData - The `data` property of the payment session. Make sure to store in it
|
* @param input - The input to get the payment status. The `data` field should contain the data from the payment provider. when the payment was created.
|
||||||
* any helpful identification for your third-party integration.
|
* @returns The payment session's status. It can also return additional `data` from the payment provider.
|
||||||
* @returns The payment session's status.
|
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* // other imports...
|
* // other imports...
|
||||||
* import {
|
* import {
|
||||||
|
* GetPaymentStatusInput,
|
||||||
|
* GetPaymentStatusOutput,
|
||||||
* PaymentSessionStatus
|
* PaymentSessionStatus
|
||||||
* } from "@medusajs/framework/types"
|
* } from "@medusajs/framework/types"
|
||||||
*
|
*
|
||||||
@@ -431,49 +369,43 @@ export abstract class AbstractPaymentProvider<TConfig = Record<string, unknown>>
|
|||||||
* Options
|
* Options
|
||||||
* > {
|
* > {
|
||||||
* async getPaymentStatus(
|
* async getPaymentStatus(
|
||||||
* paymentSessionData: Record<string, unknown>
|
* input: GetPaymentStatusInput
|
||||||
* ): Promise<PaymentSessionStatus> {
|
* ): Promise<GetPaymentStatusOutput> {
|
||||||
* const externalId = paymentSessionData.id
|
* const externalId = input.data?.id
|
||||||
*
|
*
|
||||||
* try {
|
* // assuming you have a client that retrieves the payment status
|
||||||
* // assuming you have a client that retrieves the payment status
|
* const status = await this.client.getStatus(externalId)
|
||||||
* const status = await this.client.getStatus(externalId)
|
|
||||||
*
|
*
|
||||||
* switch (status) {
|
* switch (status) {
|
||||||
* case "requires_capture":
|
* case "requires_capture":
|
||||||
* return "authorized"
|
* return {status: "authorized"}
|
||||||
* case "success":
|
* case "success":
|
||||||
* return "captured"
|
* return {status: "captured"}
|
||||||
* case "canceled":
|
* case "canceled":
|
||||||
* return "canceled"
|
* return {status: "canceled"}
|
||||||
* default:
|
* default:
|
||||||
* return "pending"
|
* return {status: "pending"}
|
||||||
* }
|
* }
|
||||||
* } catch (e) {
|
|
||||||
* return "error"
|
|
||||||
* }
|
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* // ...
|
* // ...
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
abstract getPaymentStatus(
|
abstract getPaymentStatus(
|
||||||
paymentSessionData: Record<string, unknown>
|
input: GetPaymentStatusInput
|
||||||
): Promise<PaymentSessionStatus>
|
): Promise<GetPaymentStatusOutput>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method refunds an amount of a payment previously captured.
|
* This method refunds an amount of a payment previously captured.
|
||||||
*
|
*
|
||||||
* @param paymentData - The `data` property of the payment. Make sure to store in it
|
* @param input - The input to refund the payment. The `data` field should contain the data from the payment provider. when the payment was created.
|
||||||
* any helpful identification for your third-party integration.
|
|
||||||
* @param refundAmount The amount to refund.
|
|
||||||
* @returns The new data to store in the payment's `data` property, or an error object.
|
* @returns The new data to store in the payment's `data` property, or an error object.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* // other imports...
|
* // other imports...
|
||||||
* import {
|
* import {
|
||||||
* PaymentProviderError,
|
* RefundPaymentInput,
|
||||||
* PaymentProviderSessionResponse,
|
* RefundPaymentOutput,
|
||||||
* } from "@medusajs/framework/types"
|
* } from "@medusajs/framework/types"
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
@@ -481,53 +413,36 @@ export abstract class AbstractPaymentProvider<TConfig = Record<string, unknown>>
|
|||||||
* Options
|
* Options
|
||||||
* > {
|
* > {
|
||||||
* async refundPayment(
|
* async refundPayment(
|
||||||
* paymentData: Record<string, unknown>,
|
* input: RefundPaymentInput
|
||||||
* refundAmount: number
|
* ): Promise<RefundPaymentOutput> {
|
||||||
* ): Promise<
|
* const externalId = input.data?.id
|
||||||
* PaymentProviderError | PaymentProviderSessionResponse["data"]
|
|
||||||
* > {
|
|
||||||
* const externalId = paymentData.id
|
|
||||||
*
|
*
|
||||||
* try {
|
* // assuming you have a client that refunds the payment
|
||||||
* // assuming you have a client that refunds the payment
|
* const newData = await this.client.refund(
|
||||||
* const newData = await this.client.refund(
|
|
||||||
* externalId,
|
* externalId,
|
||||||
* refundAmount
|
* input.amount
|
||||||
* )
|
* )
|
||||||
*
|
*
|
||||||
* return {
|
* return {data: newData}
|
||||||
* ...newData,
|
|
||||||
* id: externalId
|
|
||||||
* }
|
|
||||||
* } catch (e) {
|
|
||||||
* return {
|
|
||||||
* error: e,
|
|
||||||
* code: "unknown",
|
|
||||||
* detail: e
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
* }
|
||||||
*
|
|
||||||
* // ...
|
* // ...
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
abstract refundPayment(
|
abstract refundPayment(
|
||||||
paymentData: Record<string, unknown>,
|
input: RefundPaymentInput
|
||||||
refundAmount: number
|
): Promise<RefundPaymentOutput>
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the payment's data from the third-party service.
|
* Retrieves the payment's data from the third-party service.
|
||||||
*
|
*
|
||||||
* @param paymentSessionData - The `data` property of the payment. Make sure to store in it
|
* @param input - The input to retrieve the payment. The `data` field should contain the data from the payment provider when the payment was created.
|
||||||
* any helpful identification for your third-party integration.
|
* @returns The payment's data as found in the the payment provider.
|
||||||
* @returns An object to be stored in the payment's `data` property, or an error object.
|
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* // other imports...
|
* // other imports...
|
||||||
* import {
|
* import {
|
||||||
* PaymentProviderError,
|
* RetrievePaymentInput,
|
||||||
* PaymentProviderSessionResponse,
|
* RetrievePaymentOutput,
|
||||||
* } from "@medusajs/framework/types"
|
* } from "@medusajs/framework/types"
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
@@ -535,44 +450,31 @@ export abstract class AbstractPaymentProvider<TConfig = Record<string, unknown>>
|
|||||||
* Options
|
* Options
|
||||||
* > {
|
* > {
|
||||||
* async retrievePayment(
|
* async retrievePayment(
|
||||||
* paymentSessionData: Record<string, unknown>
|
* input: RetrievePaymentInput
|
||||||
* ): Promise<
|
* ): Promise<RetrievePaymentOutput> {
|
||||||
* PaymentProviderError | PaymentProviderSessionResponse["data"]
|
* const externalId = input.data?.id
|
||||||
* > {
|
|
||||||
* const externalId = paymentSessionData.id
|
|
||||||
*
|
*
|
||||||
* try {
|
* // assuming you have a client that retrieves the payment
|
||||||
* // assuming you have a client that retrieves the payment
|
* return await this.client.retrieve(externalId)
|
||||||
* return await this.client.retrieve(externalId)
|
|
||||||
* } catch (e) {
|
|
||||||
* return {
|
|
||||||
* error: e,
|
|
||||||
* code: "unknown",
|
|
||||||
* detail: e
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
* }
|
||||||
*
|
|
||||||
* // ...
|
* // ...
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
abstract retrievePayment(
|
abstract retrievePayment(
|
||||||
paymentSessionData: Record<string, unknown>
|
input: RetrievePaymentInput
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
|
): Promise<RetrievePaymentOutput>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a payment in the third-party service that was previously initiated with the {@link initiatePayment} method.
|
* Update a payment in the third-party service that was previously initiated with the {@link initiatePayment} method.
|
||||||
*
|
*
|
||||||
* @param context - The details of the payment session and its context.
|
* @param input - The input to update the payment. The `data` field should contain the data from the payment provider. when the payment was created.
|
||||||
* @returns An object whose `data` property is set in the updated payment session, or an error
|
* @returns The new data to store in the payment's `data` property. Throws in case of an error.
|
||||||
* object. Make sure to set in `data` anything useful to later retrieve the session.
|
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* // other imports...
|
* // other imports...
|
||||||
* import {
|
* import {
|
||||||
* UpdatePaymentProviderSession,
|
* UpdatePaymentInput,
|
||||||
* PaymentProviderError,
|
* UpdatePaymentOutput,
|
||||||
* PaymentProviderSessionResponse,
|
|
||||||
* } from "@medusajs/framework/types"
|
* } from "@medusajs/framework/types"
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
@@ -580,48 +482,30 @@ export abstract class AbstractPaymentProvider<TConfig = Record<string, unknown>>
|
|||||||
* Options
|
* Options
|
||||||
* > {
|
* > {
|
||||||
* async updatePayment(
|
* async updatePayment(
|
||||||
* context: UpdatePaymentProviderSession
|
* input: UpdatePaymentInput
|
||||||
* ): Promise<PaymentProviderError | PaymentProviderSessionResponse> {
|
* ): Promise<UpdatePaymentOutput> {
|
||||||
* const {
|
* const { amount, currency_code, context } = input
|
||||||
* amount,
|
* const externalId = input.data?.id
|
||||||
* currency_code,
|
|
||||||
* context: customerDetails,
|
|
||||||
* data
|
|
||||||
* } = context
|
|
||||||
* const externalId = data.id
|
|
||||||
*
|
*
|
||||||
* try {
|
* // assuming you have a client that updates the payment
|
||||||
* // assuming you have a client that updates the payment
|
* const response = await this.client.update(
|
||||||
* const response = await this.client.update(
|
* externalId,
|
||||||
* externalId,
|
|
||||||
* {
|
* {
|
||||||
* amount,
|
* amount,
|
||||||
* currency_code,
|
* currency_code,
|
||||||
* customerDetails
|
* context.customer
|
||||||
* }
|
* }
|
||||||
* )
|
* )
|
||||||
*
|
*
|
||||||
* return {
|
* return response
|
||||||
* ...response,
|
|
||||||
* data: {
|
|
||||||
* id: response.id
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* } catch (e) {
|
|
||||||
* return {
|
|
||||||
* error: e,
|
|
||||||
* code: "unknown",
|
|
||||||
* detail: e
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* // ...
|
* // ...
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
abstract updatePayment(
|
abstract updatePayment(
|
||||||
context: UpdatePaymentProviderSession
|
input: UpdatePaymentInput
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse>
|
): Promise<UpdatePaymentOutput>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is executed when a webhook event is received from the third-party payment provider. Use it
|
* This method is executed when a webhook event is received from the third-party payment provider. Use it
|
||||||
|
|||||||
@@ -11,19 +11,13 @@ export const POST = async (
|
|||||||
res: MedusaResponse<HttpTypes.StorePaymentCollectionResponse>
|
res: MedusaResponse<HttpTypes.StorePaymentCollectionResponse>
|
||||||
) => {
|
) => {
|
||||||
const collectionId = req.params.id
|
const collectionId = req.params.id
|
||||||
const { context = {}, data, provider_id } = req.body
|
const { provider_id, data } = req.body
|
||||||
|
|
||||||
// If the customer is logged in, we auto-assign them to the payment collection
|
|
||||||
if (req.auth_context?.actor_id) {
|
|
||||||
;(context as any).customer = {
|
|
||||||
id: req.auth_context?.actor_id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const workflowInput = {
|
const workflowInput = {
|
||||||
payment_collection_id: collectionId,
|
payment_collection_id: collectionId,
|
||||||
provider_id: provider_id,
|
provider_id: provider_id,
|
||||||
|
customer_id: req.auth_context?.actor_id,
|
||||||
data,
|
data,
|
||||||
context,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await createPaymentSessionsWorkflow(req.scope).run({
|
await createPaymentSessionsWorkflow(req.scope).run({
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ export type StoreCreatePaymentSessionType = z.infer<
|
|||||||
export const StoreCreatePaymentSession = z
|
export const StoreCreatePaymentSession = z
|
||||||
.object({
|
.object({
|
||||||
provider_id: z.string(),
|
provider_id: z.string(),
|
||||||
context: z.record(z.unknown()).optional(),
|
|
||||||
data: z.record(z.unknown()).optional(),
|
data: z.record(z.unknown()).optional(),
|
||||||
})
|
})
|
||||||
.strict()
|
.strict()
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import { ModuleJoinerConfig } from "@medusajs/framework/types"
|
||||||
|
import { LINKS, Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
|
export const CustomerAccountHolder: ModuleJoinerConfig = {
|
||||||
|
serviceName: LINKS.CustomerAccountHolder,
|
||||||
|
isLink: true,
|
||||||
|
databaseConfig: {
|
||||||
|
tableName: "customer_account_holder",
|
||||||
|
idPrefix: "custacchldr",
|
||||||
|
},
|
||||||
|
alias: [
|
||||||
|
{
|
||||||
|
name: ["customer_account_holder", "customer_account_holders"],
|
||||||
|
entity: "LinkCustomerAccountHolder",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
primaryKeys: ["id", "customer_id", "account_holder_id"],
|
||||||
|
relationships: [
|
||||||
|
{
|
||||||
|
serviceName: Modules.CUSTOMER,
|
||||||
|
entity: "Customer",
|
||||||
|
primaryKey: "id",
|
||||||
|
foreignKey: "customer_id",
|
||||||
|
alias: "customer",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "Customers",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceName: Modules.PAYMENT,
|
||||||
|
entity: "AccountHolder",
|
||||||
|
primaryKey: "id",
|
||||||
|
foreignKey: "account_holder_id",
|
||||||
|
alias: "account_holder",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "AccountHolders",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
extends: [
|
||||||
|
{
|
||||||
|
serviceName: Modules.CUSTOMER,
|
||||||
|
entity: "Customer",
|
||||||
|
fieldAlias: {
|
||||||
|
account_holder: "account_holder_link.account_holder",
|
||||||
|
},
|
||||||
|
relationship: {
|
||||||
|
serviceName: LINKS.CustomerAccountHolder,
|
||||||
|
primaryKey: "customer_id",
|
||||||
|
foreignKey: "id",
|
||||||
|
alias: "account_holder_link",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceName: Modules.PAYMENT,
|
||||||
|
entity: "AccountHolder",
|
||||||
|
fieldAlias: {
|
||||||
|
customer: "customer_link.customer",
|
||||||
|
},
|
||||||
|
relationship: {
|
||||||
|
serviceName: LINKS.CustomerAccountHolder,
|
||||||
|
primaryKey: "account_holder_id",
|
||||||
|
foreignKey: "id",
|
||||||
|
alias: "customer_link",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -16,3 +16,4 @@ export * from "./region-payment-provider"
|
|||||||
export * from "./sales-channel-location"
|
export * from "./sales-channel-location"
|
||||||
export * from "./shipping-option-price-set"
|
export * from "./shipping-option-price-set"
|
||||||
export * from "./product-shipping-profile"
|
export * from "./product-shipping-profile"
|
||||||
|
export * from "./customer-account-holder"
|
||||||
+21
-29
@@ -23,12 +23,13 @@ moduleIntegrationTestRunner<IPaymentModuleService>({
|
|||||||
service: PaymentModuleService,
|
service: PaymentModuleService,
|
||||||
}).linkable
|
}).linkable
|
||||||
|
|
||||||
expect(Object.keys(linkable)).toHaveLength(5)
|
expect(Object.keys(linkable)).toHaveLength(6)
|
||||||
expect(Object.keys(linkable)).toEqual([
|
expect(Object.keys(linkable)).toEqual([
|
||||||
"paymentCollection",
|
"paymentCollection",
|
||||||
"paymentSession",
|
"paymentSession",
|
||||||
"payment",
|
"payment",
|
||||||
"refundReason",
|
"refundReason",
|
||||||
|
"accountHolder",
|
||||||
"paymentProvider",
|
"paymentProvider",
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -73,6 +74,15 @@ moduleIntegrationTestRunner<IPaymentModuleService>({
|
|||||||
field: "refundReason",
|
field: "refundReason",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
accountHolder: {
|
||||||
|
id: {
|
||||||
|
linkable: "account_holder_id",
|
||||||
|
entity: "AccountHolder",
|
||||||
|
primaryKey: "id",
|
||||||
|
serviceName: "payment",
|
||||||
|
field: "accountHolder",
|
||||||
|
},
|
||||||
|
},
|
||||||
paymentProvider: {
|
paymentProvider: {
|
||||||
id: {
|
id: {
|
||||||
linkable: "payment_provider_id",
|
linkable: "payment_provider_id",
|
||||||
@@ -100,10 +110,7 @@ moduleIntegrationTestRunner<IPaymentModuleService>({
|
|||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
data: {},
|
data: {},
|
||||||
context: {
|
context: {
|
||||||
extra: {},
|
customer: { id: "cus-id-1", email: "new@test.tsst" },
|
||||||
customer: {},
|
|
||||||
billing_address: {},
|
|
||||||
email: "test@test.test.com",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -347,10 +354,10 @@ moduleIntegrationTestRunner<IPaymentModuleService>({
|
|||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
data: {},
|
data: {},
|
||||||
context: {
|
context: {
|
||||||
extra: {},
|
customer: {
|
||||||
customer: {},
|
id: "cus-id-1",
|
||||||
billing_address: {},
|
email: "test@test.test.com",
|
||||||
email: "test@test.test.com",
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -402,10 +409,7 @@ moduleIntegrationTestRunner<IPaymentModuleService>({
|
|||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
data: {},
|
data: {},
|
||||||
context: {
|
context: {
|
||||||
extra: {},
|
customer: { id: "cus-id-1", email: "test@test.test.com" },
|
||||||
customer: {},
|
|
||||||
billing_address: {},
|
|
||||||
email: "test@test.test.com",
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch((e) => e)
|
.catch((e) => e)
|
||||||
@@ -439,10 +443,7 @@ moduleIntegrationTestRunner<IPaymentModuleService>({
|
|||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
data: {},
|
data: {},
|
||||||
context: {
|
context: {
|
||||||
extra: {},
|
customer: { id: "cus-id-1", email: "test@test.test.com" },
|
||||||
customer: {},
|
|
||||||
billing_address: {},
|
|
||||||
email: "test@test.test.com",
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch((e) => e)
|
.catch((e) => e)
|
||||||
@@ -461,10 +462,7 @@ moduleIntegrationTestRunner<IPaymentModuleService>({
|
|||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
data: {},
|
data: {},
|
||||||
context: {
|
context: {
|
||||||
extra: {},
|
customer: { id: "cus-id-1", email: "new@test.tsst" },
|
||||||
customer: {},
|
|
||||||
billing_address: {},
|
|
||||||
email: "test@test.test.com",
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -474,10 +472,7 @@ moduleIntegrationTestRunner<IPaymentModuleService>({
|
|||||||
currency_code: "eur",
|
currency_code: "eur",
|
||||||
data: {},
|
data: {},
|
||||||
context: {
|
context: {
|
||||||
extra: {},
|
customer: { id: "cus-id-1", email: "new@test.tsst" },
|
||||||
customer: {},
|
|
||||||
billing_address: {},
|
|
||||||
email: "new@test.tsst",
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -505,10 +500,7 @@ moduleIntegrationTestRunner<IPaymentModuleService>({
|
|||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
data: {},
|
data: {},
|
||||||
context: {
|
context: {
|
||||||
extra: {},
|
customer: { id: "cus-id-1", email: "new@test.tsst" },
|
||||||
email: "test@test.com",
|
|
||||||
billing_address: {},
|
|
||||||
customer: {},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
PaymentProvider,
|
PaymentProvider,
|
||||||
PaymentSession,
|
PaymentSession,
|
||||||
RefundReason,
|
RefundReason,
|
||||||
|
AccountHolder,
|
||||||
} from "@models"
|
} from "@models"
|
||||||
import { default as schema } from "./schema"
|
import { default as schema } from "./schema"
|
||||||
|
|
||||||
@@ -16,12 +17,14 @@ export const joinerConfig = defineJoinerConfig(Modules.PAYMENT, {
|
|||||||
PaymentProvider,
|
PaymentProvider,
|
||||||
PaymentSession,
|
PaymentSession,
|
||||||
RefundReason,
|
RefundReason,
|
||||||
|
AccountHolder,
|
||||||
],
|
],
|
||||||
linkableKeys: {
|
linkableKeys: {
|
||||||
payment_id: Payment.name,
|
payment_id: Payment.name,
|
||||||
payment_collection_id: PaymentCollection.name,
|
payment_collection_id: PaymentCollection.name,
|
||||||
payment_provider_id: PaymentProvider.name,
|
payment_provider_id: PaymentProvider.name,
|
||||||
refund_reason_id: RefundReason.name,
|
refund_reason_id: RefundReason.name,
|
||||||
|
account_holder_id: AccountHolder.name,
|
||||||
},
|
},
|
||||||
alias: [
|
alias: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,132 @@
|
|||||||
],
|
],
|
||||||
"name": "public",
|
"name": "public",
|
||||||
"tables": [
|
"tables": [
|
||||||
|
{
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"mappedType": "text"
|
||||||
|
},
|
||||||
|
"provider_id": {
|
||||||
|
"name": "provider_id",
|
||||||
|
"type": "text",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"mappedType": "text"
|
||||||
|
},
|
||||||
|
"external_id": {
|
||||||
|
"name": "external_id",
|
||||||
|
"type": "text",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"mappedType": "text"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"name": "email",
|
||||||
|
"type": "text",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": true,
|
||||||
|
"mappedType": "text"
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"name": "data",
|
||||||
|
"type": "jsonb",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"default": "'{}'",
|
||||||
|
"mappedType": "json"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"name": "metadata",
|
||||||
|
"type": "jsonb",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": true,
|
||||||
|
"mappedType": "json"
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamptz",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"length": 6,
|
||||||
|
"default": "now()",
|
||||||
|
"mappedType": "datetime"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamptz",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"length": 6,
|
||||||
|
"default": "now()",
|
||||||
|
"mappedType": "datetime"
|
||||||
|
},
|
||||||
|
"deleted_at": {
|
||||||
|
"name": "deleted_at",
|
||||||
|
"type": "timestamptz",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": true,
|
||||||
|
"length": 6,
|
||||||
|
"mappedType": "datetime"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "account_holder",
|
||||||
|
"schema": "public",
|
||||||
|
"indexes": [
|
||||||
|
{
|
||||||
|
"keyName": "IDX_account_holder_deleted_at",
|
||||||
|
"columnNames": [],
|
||||||
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
|
"primary": false,
|
||||||
|
"unique": false,
|
||||||
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_account_holder_deleted_at\" ON \"account_holder\" (deleted_at) WHERE deleted_at IS NULL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyName": "IDX_account_holder_provider_id_external_id_unique",
|
||||||
|
"columnNames": [],
|
||||||
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
|
"primary": false,
|
||||||
|
"unique": false,
|
||||||
|
"expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_account_holder_provider_id_external_id_unique\" ON \"account_holder\" (provider_id, external_id) WHERE deleted_at IS NULL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyName": "account_holder_pkey",
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"composite": false,
|
||||||
|
"constraint": true,
|
||||||
|
"primary": true,
|
||||||
|
"unique": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"checks": [],
|
||||||
|
"foreignKeys": {},
|
||||||
|
"nativeEnums": {}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"columns": {
|
"columns": {
|
||||||
"id": {
|
"id": {
|
||||||
@@ -172,6 +298,7 @@
|
|||||||
"keyName": "IDX_payment_collection_deleted_at",
|
"keyName": "IDX_payment_collection_deleted_at",
|
||||||
"columnNames": [],
|
"columnNames": [],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false,
|
"unique": false,
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_collection_deleted_at\" ON \"payment_collection\" (deleted_at) WHERE deleted_at IS NULL"
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_collection_deleted_at\" ON \"payment_collection\" (deleted_at) WHERE deleted_at IS NULL"
|
||||||
@@ -182,12 +309,14 @@
|
|||||||
"id"
|
"id"
|
||||||
],
|
],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": true,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"checks": [],
|
"checks": [],
|
||||||
"foreignKeys": {}
|
"foreignKeys": {},
|
||||||
|
"nativeEnums": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columns": {
|
"columns": {
|
||||||
@@ -294,6 +423,7 @@
|
|||||||
"keyName": "IDX_payment_method_token_deleted_at",
|
"keyName": "IDX_payment_method_token_deleted_at",
|
||||||
"columnNames": [],
|
"columnNames": [],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false,
|
"unique": false,
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_method_token_deleted_at\" ON \"payment_method_token\" (deleted_at) WHERE deleted_at IS NULL"
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_method_token_deleted_at\" ON \"payment_method_token\" (deleted_at) WHERE deleted_at IS NULL"
|
||||||
@@ -304,12 +434,14 @@
|
|||||||
"id"
|
"id"
|
||||||
],
|
],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": true,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"checks": [],
|
"checks": [],
|
||||||
"foreignKeys": {}
|
"foreignKeys": {},
|
||||||
|
"nativeEnums": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columns": {
|
"columns": {
|
||||||
@@ -372,6 +504,7 @@
|
|||||||
"keyName": "IDX_payment_provider_deleted_at",
|
"keyName": "IDX_payment_provider_deleted_at",
|
||||||
"columnNames": [],
|
"columnNames": [],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false,
|
"unique": false,
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_provider_deleted_at\" ON \"payment_provider\" (deleted_at) WHERE deleted_at IS NULL"
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_provider_deleted_at\" ON \"payment_provider\" (deleted_at) WHERE deleted_at IS NULL"
|
||||||
@@ -382,12 +515,14 @@
|
|||||||
"id"
|
"id"
|
||||||
],
|
],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": true,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"checks": [],
|
"checks": [],
|
||||||
"foreignKeys": {}
|
"foreignKeys": {},
|
||||||
|
"nativeEnums": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columns": {
|
"columns": {
|
||||||
@@ -420,14 +555,15 @@
|
|||||||
"payment_provider_id"
|
"payment_provider_id"
|
||||||
],
|
],
|
||||||
"composite": true,
|
"composite": true,
|
||||||
|
"constraint": true,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"checks": [],
|
"checks": [],
|
||||||
"foreignKeys": {
|
"foreignKeys": {
|
||||||
"payment_collection_payment_providers_payment_coll_aa276_foreign": {
|
"payment_collection_payment_providers_payment_col_aa276_foreign": {
|
||||||
"constraintName": "payment_collection_payment_providers_payment_coll_aa276_foreign",
|
"constraintName": "payment_collection_payment_providers_payment_col_aa276_foreign",
|
||||||
"columnNames": [
|
"columnNames": [
|
||||||
"payment_collection_id"
|
"payment_collection_id"
|
||||||
],
|
],
|
||||||
@@ -439,8 +575,8 @@
|
|||||||
"deleteRule": "cascade",
|
"deleteRule": "cascade",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
},
|
},
|
||||||
"payment_collection_payment_providers_payment_provider_id_foreign": {
|
"payment_collection_payment_providers_payment_pro_2d555_foreign": {
|
||||||
"constraintName": "payment_collection_payment_providers_payment_provider_id_foreign",
|
"constraintName": "payment_collection_payment_providers_payment_pro_2d555_foreign",
|
||||||
"columnNames": [
|
"columnNames": [
|
||||||
"payment_provider_id"
|
"payment_provider_id"
|
||||||
],
|
],
|
||||||
@@ -452,7 +588,8 @@
|
|||||||
"deleteRule": "cascade",
|
"deleteRule": "cascade",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"nativeEnums": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columns": {
|
"columns": {
|
||||||
@@ -606,6 +743,7 @@
|
|||||||
"keyName": "IDX_payment_session_payment_collection_id",
|
"keyName": "IDX_payment_session_payment_collection_id",
|
||||||
"columnNames": [],
|
"columnNames": [],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false,
|
"unique": false,
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_session_payment_collection_id\" ON \"payment_session\" (payment_collection_id) WHERE deleted_at IS NULL"
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_session_payment_collection_id\" ON \"payment_session\" (payment_collection_id) WHERE deleted_at IS NULL"
|
||||||
@@ -614,6 +752,7 @@
|
|||||||
"keyName": "IDX_payment_session_deleted_at",
|
"keyName": "IDX_payment_session_deleted_at",
|
||||||
"columnNames": [],
|
"columnNames": [],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false,
|
"unique": false,
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_session_deleted_at\" ON \"payment_session\" (deleted_at) WHERE deleted_at IS NULL"
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_session_deleted_at\" ON \"payment_session\" (deleted_at) WHERE deleted_at IS NULL"
|
||||||
@@ -624,6 +763,7 @@
|
|||||||
"id"
|
"id"
|
||||||
],
|
],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": true,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
}
|
}
|
||||||
@@ -643,7 +783,8 @@
|
|||||||
"deleteRule": "cascade",
|
"deleteRule": "cascade",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"nativeEnums": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columns": {
|
"columns": {
|
||||||
@@ -788,6 +929,7 @@
|
|||||||
"keyName": "IDX_payment_payment_collection_id",
|
"keyName": "IDX_payment_payment_collection_id",
|
||||||
"columnNames": [],
|
"columnNames": [],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false,
|
"unique": false,
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_payment_collection_id\" ON \"payment\" (payment_collection_id) WHERE deleted_at IS NULL"
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_payment_collection_id\" ON \"payment\" (payment_collection_id) WHERE deleted_at IS NULL"
|
||||||
@@ -796,6 +938,7 @@
|
|||||||
"keyName": "IDX_payment_payment_session_id_unique",
|
"keyName": "IDX_payment_payment_session_id_unique",
|
||||||
"columnNames": [],
|
"columnNames": [],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false,
|
"unique": false,
|
||||||
"expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_payment_payment_session_id_unique\" ON \"payment\" (payment_session_id) WHERE deleted_at IS NULL"
|
"expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_payment_payment_session_id_unique\" ON \"payment\" (payment_session_id) WHERE deleted_at IS NULL"
|
||||||
@@ -804,6 +947,7 @@
|
|||||||
"keyName": "IDX_payment_deleted_at",
|
"keyName": "IDX_payment_deleted_at",
|
||||||
"columnNames": [],
|
"columnNames": [],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false,
|
"unique": false,
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_deleted_at\" ON \"payment\" (deleted_at) WHERE deleted_at IS NULL"
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_deleted_at\" ON \"payment\" (deleted_at) WHERE deleted_at IS NULL"
|
||||||
@@ -812,6 +956,7 @@
|
|||||||
"keyName": "IDX_payment_provider_id",
|
"keyName": "IDX_payment_provider_id",
|
||||||
"columnNames": [],
|
"columnNames": [],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false,
|
"unique": false,
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_provider_id\" ON \"payment\" (provider_id) WHERE deleted_at IS NULL"
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_provider_id\" ON \"payment\" (provider_id) WHERE deleted_at IS NULL"
|
||||||
@@ -820,6 +965,7 @@
|
|||||||
"keyName": "IDX_payment_payment_session_id",
|
"keyName": "IDX_payment_payment_session_id",
|
||||||
"columnNames": [],
|
"columnNames": [],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false,
|
"unique": false,
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_payment_session_id\" ON \"payment\" (payment_session_id) WHERE deleted_at IS NULL"
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_payment_payment_session_id\" ON \"payment\" (payment_session_id) WHERE deleted_at IS NULL"
|
||||||
@@ -830,6 +976,7 @@
|
|||||||
"id"
|
"id"
|
||||||
],
|
],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": true,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
}
|
}
|
||||||
@@ -861,7 +1008,8 @@
|
|||||||
"referencedTableName": "public.payment_session",
|
"referencedTableName": "public.payment_session",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"nativeEnums": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columns": {
|
"columns": {
|
||||||
@@ -959,6 +1107,7 @@
|
|||||||
"keyName": "IDX_capture_payment_id",
|
"keyName": "IDX_capture_payment_id",
|
||||||
"columnNames": [],
|
"columnNames": [],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false,
|
"unique": false,
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_capture_payment_id\" ON \"capture\" (payment_id) WHERE deleted_at IS NULL"
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_capture_payment_id\" ON \"capture\" (payment_id) WHERE deleted_at IS NULL"
|
||||||
@@ -967,6 +1116,7 @@
|
|||||||
"keyName": "IDX_capture_deleted_at",
|
"keyName": "IDX_capture_deleted_at",
|
||||||
"columnNames": [],
|
"columnNames": [],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false,
|
"unique": false,
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_capture_deleted_at\" ON \"capture\" (deleted_at) WHERE deleted_at IS NULL"
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_capture_deleted_at\" ON \"capture\" (deleted_at) WHERE deleted_at IS NULL"
|
||||||
@@ -977,6 +1127,7 @@
|
|||||||
"id"
|
"id"
|
||||||
],
|
],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": true,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
}
|
}
|
||||||
@@ -996,7 +1147,8 @@
|
|||||||
"deleteRule": "cascade",
|
"deleteRule": "cascade",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"nativeEnums": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columns": {
|
"columns": {
|
||||||
@@ -1076,6 +1228,7 @@
|
|||||||
"keyName": "IDX_refund_reason_deleted_at",
|
"keyName": "IDX_refund_reason_deleted_at",
|
||||||
"columnNames": [],
|
"columnNames": [],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false,
|
"unique": false,
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_refund_reason_deleted_at\" ON \"refund_reason\" (deleted_at) WHERE deleted_at IS NULL"
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_refund_reason_deleted_at\" ON \"refund_reason\" (deleted_at) WHERE deleted_at IS NULL"
|
||||||
@@ -1086,12 +1239,14 @@
|
|||||||
"id"
|
"id"
|
||||||
],
|
],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": true,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"checks": [],
|
"checks": [],
|
||||||
"foreignKeys": {}
|
"foreignKeys": {},
|
||||||
|
"nativeEnums": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columns": {
|
"columns": {
|
||||||
@@ -1207,6 +1362,7 @@
|
|||||||
"keyName": "IDX_refund_payment_id",
|
"keyName": "IDX_refund_payment_id",
|
||||||
"columnNames": [],
|
"columnNames": [],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false,
|
"unique": false,
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_refund_payment_id\" ON \"refund\" (payment_id) WHERE deleted_at IS NULL"
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_refund_payment_id\" ON \"refund\" (payment_id) WHERE deleted_at IS NULL"
|
||||||
@@ -1215,6 +1371,7 @@
|
|||||||
"keyName": "IDX_refund_refund_reason_id",
|
"keyName": "IDX_refund_refund_reason_id",
|
||||||
"columnNames": [],
|
"columnNames": [],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false,
|
"unique": false,
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_refund_refund_reason_id\" ON \"refund\" (refund_reason_id) WHERE deleted_at IS NULL"
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_refund_refund_reason_id\" ON \"refund\" (refund_reason_id) WHERE deleted_at IS NULL"
|
||||||
@@ -1223,6 +1380,7 @@
|
|||||||
"keyName": "IDX_refund_deleted_at",
|
"keyName": "IDX_refund_deleted_at",
|
||||||
"columnNames": [],
|
"columnNames": [],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false,
|
"unique": false,
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_refund_deleted_at\" ON \"refund\" (deleted_at) WHERE deleted_at IS NULL"
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_refund_deleted_at\" ON \"refund\" (deleted_at) WHERE deleted_at IS NULL"
|
||||||
@@ -1233,6 +1391,7 @@
|
|||||||
"id"
|
"id"
|
||||||
],
|
],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
|
"constraint": true,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
}
|
}
|
||||||
@@ -1265,7 +1424,9 @@
|
|||||||
"deleteRule": "set null",
|
"deleteRule": "set null",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"nativeEnums": {}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"nativeEnums": {}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Migration } from '@mikro-orm/migrations';
|
||||||
|
|
||||||
|
export class Migration20250123122334 extends Migration {
|
||||||
|
|
||||||
|
override async up(): Promise<void> {
|
||||||
|
this.addSql(`create table if not exists "account_holder" ("id" text not null, "provider_id" text not null, "external_id" text not null, "email" text null, "data" jsonb not null default '{}', "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "account_holder_pkey" primary key ("id"));`);
|
||||||
|
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_account_holder_deleted_at" ON "account_holder" (deleted_at) WHERE deleted_at IS NULL;`);
|
||||||
|
this.addSql(`CREATE UNIQUE INDEX IF NOT EXISTS "IDX_account_holder_provider_id_external_id_unique" ON "account_holder" (provider_id, external_id) WHERE deleted_at IS NULL;`);
|
||||||
|
|
||||||
|
this.addSql(`alter table if exists "payment_collection_payment_providers" drop constraint if exists "payment_collection_payment_providers_payment_coll_aa276_foreign";`);
|
||||||
|
this.addSql(`alter table if exists "payment_collection_payment_providers" drop constraint if exists "payment_collection_payment_providers_payment_provider_id_foreign";`);
|
||||||
|
|
||||||
|
this.addSql(`alter table if exists "payment_collection_payment_providers" add constraint "payment_collection_payment_providers_payment_col_aa276_foreign" foreign key ("payment_collection_id") references "payment_collection" ("id") on update cascade on delete cascade;`);
|
||||||
|
this.addSql(`alter table if exists "payment_collection_payment_providers" add constraint "payment_collection_payment_providers_payment_pro_2d555_foreign" foreign key ("payment_provider_id") references "payment_provider" ("id") on update cascade on delete cascade;`);
|
||||||
|
}
|
||||||
|
|
||||||
|
override async down(): Promise<void> {
|
||||||
|
this.addSql(`drop table if exists "account_holder" cascade;`);
|
||||||
|
|
||||||
|
this.addSql(`alter table if exists "payment_collection_payment_providers" drop constraint if exists "payment_collection_payment_providers_payment_col_aa276_foreign";`);
|
||||||
|
this.addSql(`alter table if exists "payment_collection_payment_providers" drop constraint if exists "payment_collection_payment_providers_payment_pro_2d555_foreign";`);
|
||||||
|
|
||||||
|
this.addSql(`alter table if exists "payment_collection_payment_providers" add constraint "payment_collection_payment_providers_payment_coll_aa276_foreign" foreign key ("payment_collection_id") references "payment_collection" ("id") on update cascade on delete cascade;`);
|
||||||
|
this.addSql(`alter table if exists "payment_collection_payment_providers" add constraint "payment_collection_payment_providers_payment_provider_id_foreign" foreign key ("payment_provider_id") references "payment_provider" ("id") on update cascade on delete cascade;`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { model } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
|
const AccountHolder = model
|
||||||
|
.define("AccountHolder", {
|
||||||
|
id: model.id({ prefix: "acchld" }).primaryKey(),
|
||||||
|
provider_id: model.text(),
|
||||||
|
external_id: model.text(),
|
||||||
|
email: model.text().nullable(),
|
||||||
|
data: model.json().default({}),
|
||||||
|
metadata: model.json().nullable(),
|
||||||
|
})
|
||||||
|
.indexes([
|
||||||
|
{
|
||||||
|
on: ["provider_id", "external_id"],
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
export default AccountHolder
|
||||||
@@ -6,3 +6,4 @@ export { default as PaymentProvider } from "./payment-provider"
|
|||||||
export { default as PaymentSession } from "./payment-session"
|
export { default as PaymentSession } from "./payment-session"
|
||||||
export { default as Refund } from "./refund"
|
export { default as Refund } from "./refund"
|
||||||
export { default as RefundReason } from "./refund-reason"
|
export { default as RefundReason } from "./refund-reason"
|
||||||
|
export { default as AccountHolder } from "./account-holder"
|
||||||
@@ -1,8 +1,25 @@
|
|||||||
|
import crypto from "crypto"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CreatePaymentProviderSession,
|
AuthorizePaymentInput,
|
||||||
PaymentProviderError,
|
AuthorizePaymentOutput,
|
||||||
PaymentProviderSessionResponse,
|
CancelPaymentInput,
|
||||||
|
CancelPaymentOutput,
|
||||||
|
CapturePaymentInput,
|
||||||
|
CapturePaymentOutput,
|
||||||
|
DeletePaymentInput,
|
||||||
|
DeletePaymentOutput,
|
||||||
|
GetPaymentStatusInput,
|
||||||
|
GetPaymentStatusOutput,
|
||||||
|
InitiatePaymentInput,
|
||||||
|
InitiatePaymentOutput,
|
||||||
ProviderWebhookPayload,
|
ProviderWebhookPayload,
|
||||||
|
RefundPaymentInput,
|
||||||
|
RefundPaymentOutput,
|
||||||
|
RetrievePaymentInput,
|
||||||
|
RetrievePaymentOutput,
|
||||||
|
UpdatePaymentInput,
|
||||||
|
UpdatePaymentOutput,
|
||||||
WebhookActionResult,
|
WebhookActionResult,
|
||||||
} from "@medusajs/framework/types"
|
} from "@medusajs/framework/types"
|
||||||
import {
|
import {
|
||||||
@@ -23,53 +40,49 @@ export class SystemProviderService extends AbstractPaymentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async initiatePayment(
|
async initiatePayment(
|
||||||
context: CreatePaymentProviderSession
|
input: InitiatePaymentInput
|
||||||
): Promise<PaymentProviderSessionResponse> {
|
): Promise<InitiatePaymentOutput> {
|
||||||
return { data: {} }
|
return { data: {}, id: crypto.randomUUID() }
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPaymentStatus(
|
async getPaymentStatus(
|
||||||
paymentSessionData: Record<string, unknown>
|
input: GetPaymentStatusInput
|
||||||
): Promise<PaymentSessionStatus> {
|
): Promise<GetPaymentStatusOutput> {
|
||||||
throw new Error("Method not implemented.")
|
throw new Error("Method not implemented.")
|
||||||
}
|
}
|
||||||
|
|
||||||
async retrievePayment(
|
async retrievePayment(
|
||||||
paymentSessionData: Record<string, unknown>
|
input: RetrievePaymentInput
|
||||||
): Promise<Record<string, unknown> | PaymentProviderError> {
|
): Promise<RetrievePaymentOutput> {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
async authorizePayment(_): Promise<
|
async authorizePayment(
|
||||||
| PaymentProviderError
|
input: AuthorizePaymentInput
|
||||||
| {
|
): Promise<AuthorizePaymentOutput> {
|
||||||
status: PaymentSessionStatus
|
|
||||||
data: PaymentProviderSessionResponse["data"]
|
|
||||||
}
|
|
||||||
> {
|
|
||||||
return { data: {}, status: PaymentSessionStatus.AUTHORIZED }
|
return { data: {}, status: PaymentSessionStatus.AUTHORIZED }
|
||||||
}
|
}
|
||||||
|
|
||||||
async updatePayment(
|
async updatePayment(input: UpdatePaymentInput): Promise<UpdatePaymentOutput> {
|
||||||
_
|
return { data: {} }
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse> {
|
|
||||||
return { data: {} } as PaymentProviderSessionResponse
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async deletePayment(_): Promise<Record<string, unknown>> {
|
async deletePayment(input: DeletePaymentInput): Promise<DeletePaymentOutput> {
|
||||||
return {}
|
return { data: {} }
|
||||||
}
|
}
|
||||||
|
|
||||||
async capturePayment(_): Promise<Record<string, unknown>> {
|
async capturePayment(
|
||||||
return {}
|
input: CapturePaymentInput
|
||||||
|
): Promise<CapturePaymentOutput> {
|
||||||
|
return { data: {} }
|
||||||
}
|
}
|
||||||
|
|
||||||
async refundPayment(_): Promise<Record<string, unknown>> {
|
async refundPayment(input: RefundPaymentInput): Promise<RefundPaymentOutput> {
|
||||||
return {}
|
return { data: {} }
|
||||||
}
|
}
|
||||||
|
|
||||||
async cancelPayment(_): Promise<Record<string, unknown>> {
|
async cancelPayment(input: CancelPaymentInput): Promise<CancelPaymentOutput> {
|
||||||
return {}
|
return { data: {} }
|
||||||
}
|
}
|
||||||
|
|
||||||
async getWebhookActionAndData(
|
async getWebhookActionAndData(
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
CreatePaymentMethodDTO,
|
CreatePaymentMethodDTO,
|
||||||
CreatePaymentSessionDTO,
|
CreatePaymentSessionDTO,
|
||||||
CreateRefundDTO,
|
CreateRefundDTO,
|
||||||
|
AccountHolderDTO,
|
||||||
DAL,
|
DAL,
|
||||||
FilterablePaymentCollectionProps,
|
FilterablePaymentCollectionProps,
|
||||||
FilterablePaymentMethodProps,
|
FilterablePaymentMethodProps,
|
||||||
@@ -31,13 +32,17 @@ import {
|
|||||||
UpdatePaymentCollectionDTO,
|
UpdatePaymentCollectionDTO,
|
||||||
UpdatePaymentDTO,
|
UpdatePaymentDTO,
|
||||||
UpdatePaymentSessionDTO,
|
UpdatePaymentSessionDTO,
|
||||||
|
CreateAccountHolderDTO,
|
||||||
UpsertPaymentCollectionDTO,
|
UpsertPaymentCollectionDTO,
|
||||||
WebhookActionResult,
|
WebhookActionResult,
|
||||||
|
CreateAccountHolderOutput,
|
||||||
|
InitiatePaymentOutput,
|
||||||
} from "@medusajs/framework/types"
|
} from "@medusajs/framework/types"
|
||||||
import {
|
import {
|
||||||
BigNumber,
|
BigNumber,
|
||||||
InjectManager,
|
InjectManager,
|
||||||
InjectTransactionManager,
|
InjectTransactionManager,
|
||||||
|
isPresent,
|
||||||
isString,
|
isString,
|
||||||
MathBN,
|
MathBN,
|
||||||
MedusaContext,
|
MedusaContext,
|
||||||
@@ -48,6 +53,7 @@ import {
|
|||||||
promiseAll,
|
promiseAll,
|
||||||
} from "@medusajs/framework/utils"
|
} from "@medusajs/framework/utils"
|
||||||
import {
|
import {
|
||||||
|
AccountHolder,
|
||||||
Capture,
|
Capture,
|
||||||
Payment,
|
Payment,
|
||||||
PaymentCollection,
|
PaymentCollection,
|
||||||
@@ -66,6 +72,7 @@ type InjectedDependencies = {
|
|||||||
refundService: ModulesSdkTypes.IMedusaInternalService<any>
|
refundService: ModulesSdkTypes.IMedusaInternalService<any>
|
||||||
paymentSessionService: ModulesSdkTypes.IMedusaInternalService<any>
|
paymentSessionService: ModulesSdkTypes.IMedusaInternalService<any>
|
||||||
paymentCollectionService: ModulesSdkTypes.IMedusaInternalService<any>
|
paymentCollectionService: ModulesSdkTypes.IMedusaInternalService<any>
|
||||||
|
accountHolderService: ModulesSdkTypes.IMedusaInternalService<any>
|
||||||
paymentProviderService: PaymentProviderService
|
paymentProviderService: PaymentProviderService
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,6 +83,7 @@ const generateMethodForModels = {
|
|||||||
Capture,
|
Capture,
|
||||||
Refund,
|
Refund,
|
||||||
RefundReason,
|
RefundReason,
|
||||||
|
AccountHolder,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class PaymentModuleService
|
export default class PaymentModuleService
|
||||||
@@ -86,6 +94,7 @@ export default class PaymentModuleService
|
|||||||
Capture: { dto: CaptureDTO }
|
Capture: { dto: CaptureDTO }
|
||||||
Refund: { dto: RefundDTO }
|
Refund: { dto: RefundDTO }
|
||||||
RefundReason: { dto: RefundReasonDTO }
|
RefundReason: { dto: RefundReasonDTO }
|
||||||
|
AccountHolder: { dto: AccountHolderDTO }
|
||||||
}>(generateMethodForModels)
|
}>(generateMethodForModels)
|
||||||
implements IPaymentModuleService
|
implements IPaymentModuleService
|
||||||
{
|
{
|
||||||
@@ -107,6 +116,9 @@ export default class PaymentModuleService
|
|||||||
typeof PaymentCollection
|
typeof PaymentCollection
|
||||||
>
|
>
|
||||||
protected paymentProviderService_: PaymentProviderService
|
protected paymentProviderService_: PaymentProviderService
|
||||||
|
protected accountHolderService_: ModulesSdkTypes.IMedusaInternalService<
|
||||||
|
typeof AccountHolder
|
||||||
|
>
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
{
|
{
|
||||||
@@ -117,6 +129,7 @@ export default class PaymentModuleService
|
|||||||
paymentSessionService,
|
paymentSessionService,
|
||||||
paymentProviderService,
|
paymentProviderService,
|
||||||
paymentCollectionService,
|
paymentCollectionService,
|
||||||
|
accountHolderService,
|
||||||
}: InjectedDependencies,
|
}: InjectedDependencies,
|
||||||
protected readonly moduleDeclaration: InternalModuleDeclaration
|
protected readonly moduleDeclaration: InternalModuleDeclaration
|
||||||
) {
|
) {
|
||||||
@@ -131,6 +144,7 @@ export default class PaymentModuleService
|
|||||||
this.paymentSessionService_ = paymentSessionService
|
this.paymentSessionService_ = paymentSessionService
|
||||||
this.paymentProviderService_ = paymentProviderService
|
this.paymentProviderService_ = paymentProviderService
|
||||||
this.paymentCollectionService_ = paymentCollectionService
|
this.paymentCollectionService_ = paymentCollectionService
|
||||||
|
this.accountHolderService_ = accountHolderService
|
||||||
}
|
}
|
||||||
|
|
||||||
__joinerConfig(): ModuleJoinerConfig {
|
__joinerConfig(): ModuleJoinerConfig {
|
||||||
@@ -316,7 +330,7 @@ export default class PaymentModuleService
|
|||||||
@MedusaContext() sharedContext?: Context
|
@MedusaContext() sharedContext?: Context
|
||||||
): Promise<PaymentSessionDTO> {
|
): Promise<PaymentSessionDTO> {
|
||||||
let paymentSession: InferEntityType<typeof PaymentSession> | undefined
|
let paymentSession: InferEntityType<typeof PaymentSession> | undefined
|
||||||
let providerPaymentSession: Record<string, unknown> | undefined
|
let providerPaymentSession: InitiatePaymentOutput | undefined
|
||||||
|
|
||||||
try {
|
try {
|
||||||
paymentSession = await this.createPaymentSession_(
|
paymentSession = await this.createPaymentSession_(
|
||||||
@@ -328,7 +342,8 @@ export default class PaymentModuleService
|
|||||||
providerPaymentSession = await this.paymentProviderService_.createSession(
|
providerPaymentSession = await this.paymentProviderService_.createSession(
|
||||||
input.provider_id,
|
input.provider_id,
|
||||||
{
|
{
|
||||||
context: { ...input.context, session_id: paymentSession!.id },
|
context: input.context,
|
||||||
|
data: { ...input.data, session_id: paymentSession!.id },
|
||||||
amount: input.amount,
|
amount: input.amount,
|
||||||
currency_code: input.currency_code,
|
currency_code: input.currency_code,
|
||||||
}
|
}
|
||||||
@@ -338,15 +353,14 @@ export default class PaymentModuleService
|
|||||||
await this.paymentSessionService_.update(
|
await this.paymentSessionService_.update(
|
||||||
{
|
{
|
||||||
id: paymentSession!.id,
|
id: paymentSession!.id,
|
||||||
data: { ...input.data, ...providerPaymentSession },
|
data: { ...input.data, ...providerPaymentSession.data },
|
||||||
},
|
},
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
)[0]
|
)[0]
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (providerPaymentSession) {
|
if (providerPaymentSession) {
|
||||||
await this.paymentProviderService_.deleteSession({
|
await this.paymentProviderService_.deleteSession(input.provider_id, {
|
||||||
provider_id: input.provider_id,
|
|
||||||
data: input.data,
|
data: input.data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -420,8 +434,7 @@ export default class PaymentModuleService
|
|||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
await this.paymentProviderService_.deleteSession({
|
await this.paymentProviderService_.deleteSession(session.provider_id, {
|
||||||
provider_id: session.provider_id,
|
|
||||||
data: session.data,
|
data: session.data,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -460,11 +473,11 @@ export default class PaymentModuleService
|
|||||||
}
|
}
|
||||||
|
|
||||||
let { data, status } = await this.paymentProviderService_.authorizePayment(
|
let { data, status } = await this.paymentProviderService_.authorizePayment(
|
||||||
|
session.provider_id,
|
||||||
{
|
{
|
||||||
provider_id: session.provider_id,
|
|
||||||
data: session.data,
|
data: session.data,
|
||||||
},
|
context,
|
||||||
context
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -486,8 +499,7 @@ export default class PaymentModuleService
|
|||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await this.paymentProviderService_.cancelPayment({
|
await this.paymentProviderService_.cancelPayment(session.provider_id, {
|
||||||
provider_id: session.provider_id,
|
|
||||||
data,
|
data,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -509,7 +521,7 @@ export default class PaymentModuleService
|
|||||||
@InjectTransactionManager()
|
@InjectTransactionManager()
|
||||||
async authorizePaymentSession_(
|
async authorizePaymentSession_(
|
||||||
session: InferEntityType<typeof PaymentSession>,
|
session: InferEntityType<typeof PaymentSession>,
|
||||||
data: Record<string, unknown>,
|
data: Record<string, unknown> | undefined,
|
||||||
status: PaymentSessionStatus,
|
status: PaymentSessionStatus,
|
||||||
@MedusaContext() sharedContext?: Context
|
@MedusaContext() sharedContext?: Context
|
||||||
): Promise<InferEntityType<typeof Payment>> {
|
): Promise<InferEntityType<typeof Payment>> {
|
||||||
@@ -711,15 +723,17 @@ export default class PaymentModuleService
|
|||||||
isFullyCaptured: boolean,
|
isFullyCaptured: boolean,
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
) {
|
) {
|
||||||
const paymentData = await this.paymentProviderService_.capturePayment({
|
const paymentData = await this.paymentProviderService_.capturePayment(
|
||||||
data: payment.data!,
|
payment.provider_id,
|
||||||
provider_id: payment.provider_id,
|
{
|
||||||
})
|
data: payment.data!,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
await this.paymentService_.update(
|
await this.paymentService_.update(
|
||||||
{
|
{
|
||||||
id: payment.id,
|
id: payment.id,
|
||||||
data: paymentData,
|
data: paymentData.data,
|
||||||
captured_at: isFullyCaptured ? new Date() : undefined,
|
captured_at: isFullyCaptured ? new Date() : undefined,
|
||||||
},
|
},
|
||||||
sharedContext
|
sharedContext
|
||||||
@@ -817,15 +831,15 @@ export default class PaymentModuleService
|
|||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
) {
|
) {
|
||||||
const paymentData = await this.paymentProviderService_.refundPayment(
|
const paymentData = await this.paymentProviderService_.refundPayment(
|
||||||
|
payment.provider_id,
|
||||||
{
|
{
|
||||||
data: payment.data!,
|
data: payment.data!,
|
||||||
provider_id: payment.provider_id,
|
amount: refund.raw_amount as BigNumberInput,
|
||||||
},
|
}
|
||||||
refund.raw_amount as BigNumberInput
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await this.paymentService_.update(
|
await this.paymentService_.update(
|
||||||
{ id: payment.id, data: paymentData },
|
{ id: payment.id, data: paymentData.data },
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -843,9 +857,8 @@ export default class PaymentModuleService
|
|||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
await this.paymentProviderService_.cancelPayment({
|
await this.paymentProviderService_.cancelPayment(payment.provider_id, {
|
||||||
data: payment.data!,
|
data: payment.data!,
|
||||||
provider_id: payment.provider_id,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
await this.paymentService_.update(
|
await this.paymentService_.update(
|
||||||
@@ -909,6 +922,82 @@ export default class PaymentModuleService
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InjectManager()
|
||||||
|
async createAccountHolder(
|
||||||
|
input: CreateAccountHolderDTO,
|
||||||
|
@MedusaContext() sharedContext?: Context
|
||||||
|
): Promise<AccountHolderDTO> {
|
||||||
|
if (input.context?.account_holder) {
|
||||||
|
return input.context.account_holder as AccountHolderDTO
|
||||||
|
}
|
||||||
|
|
||||||
|
let accountHolder: InferEntityType<typeof AccountHolder> | undefined
|
||||||
|
let providerAccountHolder: CreateAccountHolderOutput | undefined
|
||||||
|
|
||||||
|
try {
|
||||||
|
providerAccountHolder =
|
||||||
|
await this.paymentProviderService_.createAccountHolder(
|
||||||
|
input.provider_id,
|
||||||
|
{ context: input.context }
|
||||||
|
)
|
||||||
|
|
||||||
|
// This can be empty when either the method is not supported or an account holder wasn't created
|
||||||
|
if (isPresent(providerAccountHolder)) {
|
||||||
|
accountHolder = await this.accountHolderService_.create(
|
||||||
|
{
|
||||||
|
external_id: providerAccountHolder.id,
|
||||||
|
email: input.context.customer?.email,
|
||||||
|
data: providerAccountHolder.data,
|
||||||
|
provider_id: input.provider_id,
|
||||||
|
},
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (providerAccountHolder) {
|
||||||
|
await this.paymentProviderService_.deleteAccountHolder(
|
||||||
|
input.provider_id,
|
||||||
|
{
|
||||||
|
context: {
|
||||||
|
account_holder: providerAccountHolder as {
|
||||||
|
data: Record<string, unknown>
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accountHolder) {
|
||||||
|
await this.accountHolderService_.delete(accountHolder.id, sharedContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.baseRepository_.serialize(accountHolder)
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectManager()
|
||||||
|
async deleteAccountHolder(
|
||||||
|
id: string,
|
||||||
|
@MedusaContext() sharedContext?: Context
|
||||||
|
): Promise<void> {
|
||||||
|
const accountHolder = await this.accountHolderService_.retrieve(
|
||||||
|
id,
|
||||||
|
{ select: ["id", "provider_id", "external_id", "email", "data"] },
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
await this.accountHolderService_.delete(id, sharedContext)
|
||||||
|
|
||||||
|
await this.paymentProviderService_.deleteAccountHolder(
|
||||||
|
accountHolder.provider_id,
|
||||||
|
{
|
||||||
|
context: { account_holder: accountHolder },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@InjectManager()
|
@InjectManager()
|
||||||
async listPaymentMethods(
|
async listPaymentMethods(
|
||||||
filters: FilterablePaymentMethodProps,
|
filters: FilterablePaymentMethodProps,
|
||||||
@@ -917,12 +1006,12 @@ export default class PaymentModuleService
|
|||||||
): Promise<PaymentMethodDTO[]> {
|
): Promise<PaymentMethodDTO[]> {
|
||||||
const res = await this.paymentProviderService_.listPaymentMethods(
|
const res = await this.paymentProviderService_.listPaymentMethods(
|
||||||
filters.provider_id,
|
filters.provider_id,
|
||||||
filters.context
|
{ context: filters.context }
|
||||||
)
|
)
|
||||||
|
|
||||||
return res.map((item) => ({
|
return res.map((item) => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
data: item.data,
|
data: item.data!,
|
||||||
provider_id: filters.provider_id,
|
provider_id: filters.provider_id,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@@ -936,12 +1025,12 @@ export default class PaymentModuleService
|
|||||||
const paymentMethods =
|
const paymentMethods =
|
||||||
await this.paymentProviderService_.listPaymentMethods(
|
await this.paymentProviderService_.listPaymentMethods(
|
||||||
filters.provider_id,
|
filters.provider_id,
|
||||||
filters.context
|
{ context: filters.context }
|
||||||
)
|
)
|
||||||
|
|
||||||
const normalizedResponse = paymentMethods.map((item) => ({
|
const normalizedResponse = paymentMethods.map((item) => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
data: item.data,
|
data: item.data!,
|
||||||
provider_id: filters.provider_id,
|
provider_id: filters.provider_id,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@@ -950,9 +1039,9 @@ export default class PaymentModuleService
|
|||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
createPaymentMethods(
|
createPaymentMethods(
|
||||||
data: CreatePaymentCollectionDTO,
|
data: CreatePaymentMethodDTO,
|
||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<PaymentCollectionDTO>
|
): Promise<PaymentMethodDTO>
|
||||||
|
|
||||||
createPaymentMethods(
|
createPaymentMethods(
|
||||||
data: CreatePaymentMethodDTO[],
|
data: CreatePaymentMethodDTO[],
|
||||||
@@ -975,7 +1064,7 @@ export default class PaymentModuleService
|
|||||||
const normalizedResponse = result.map((item, i) => {
|
const normalizedResponse = result.map((item, i) => {
|
||||||
return {
|
return {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
data: item.data,
|
data: item.data!,
|
||||||
provider_id: input[i].provider_id,
|
provider_id: input[i].provider_id,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,25 +1,36 @@
|
|||||||
import {
|
import {
|
||||||
BigNumberInput,
|
AuthorizePaymentInput,
|
||||||
CreatePaymentProviderSession,
|
AuthorizePaymentOutput,
|
||||||
|
CancelPaymentInput,
|
||||||
|
CancelPaymentOutput,
|
||||||
|
CapturePaymentInput,
|
||||||
|
CapturePaymentOutput,
|
||||||
|
CreateAccountHolderInput,
|
||||||
|
CreateAccountHolderOutput,
|
||||||
DAL,
|
DAL,
|
||||||
|
DeleteAccountHolderInput,
|
||||||
|
DeleteAccountHolderOutput,
|
||||||
|
DeletePaymentInput,
|
||||||
|
DeletePaymentOutput,
|
||||||
|
GetPaymentStatusInput,
|
||||||
|
GetPaymentStatusOutput,
|
||||||
|
InitiatePaymentInput,
|
||||||
|
InitiatePaymentOutput,
|
||||||
IPaymentProvider,
|
IPaymentProvider,
|
||||||
|
ListPaymentMethodsInput,
|
||||||
|
ListPaymentMethodsOutput,
|
||||||
Logger,
|
Logger,
|
||||||
PaymentMethodResponse,
|
|
||||||
PaymentProviderAuthorizeResponse,
|
|
||||||
PaymentProviderContext,
|
|
||||||
PaymentProviderDataInput,
|
|
||||||
PaymentProviderError,
|
|
||||||
PaymentProviderSessionResponse,
|
|
||||||
PaymentSessionStatus,
|
|
||||||
ProviderWebhookPayload,
|
ProviderWebhookPayload,
|
||||||
SavePaymentMethod,
|
RefundPaymentInput,
|
||||||
SavePaymentMethodResponse,
|
RefundPaymentOutput,
|
||||||
UpdatePaymentProviderSession,
|
SavePaymentMethodInput,
|
||||||
|
SavePaymentMethodOutput,
|
||||||
|
UpdatePaymentInput,
|
||||||
|
UpdatePaymentOutput,
|
||||||
WebhookActionResult,
|
WebhookActionResult,
|
||||||
} from "@medusajs/framework/types"
|
} from "@medusajs/framework/types"
|
||||||
import { MedusaError, ModulesSdkUtils } from "@medusajs/framework/utils"
|
import { ModulesSdkUtils } from "@medusajs/framework/utils"
|
||||||
import { PaymentProvider } from "@models"
|
import { PaymentProvider } from "@models"
|
||||||
import { EOL } from "os"
|
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
logger?: Logger
|
logger?: Logger
|
||||||
@@ -59,135 +70,128 @@ Please make sure that the provider is registered in the container and it is conf
|
|||||||
|
|
||||||
async createSession(
|
async createSession(
|
||||||
providerId: string,
|
providerId: string,
|
||||||
sessionInput: CreatePaymentProviderSession
|
sessionInput: InitiatePaymentInput
|
||||||
): Promise<PaymentProviderSessionResponse["data"]> {
|
): Promise<InitiatePaymentOutput> {
|
||||||
const provider = this.retrieveProvider(providerId)
|
const provider = this.retrieveProvider(providerId)
|
||||||
|
|
||||||
const paymentResponse = await provider.initiatePayment(sessionInput)
|
return await provider.initiatePayment(sessionInput)
|
||||||
|
|
||||||
if (isPaymentProviderError(paymentResponse)) {
|
|
||||||
this.throwPaymentProviderError(paymentResponse)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (paymentResponse as PaymentProviderSessionResponse).data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateSession(
|
async updateSession(
|
||||||
providerId: string,
|
providerId: string,
|
||||||
sessionInput: UpdatePaymentProviderSession
|
sessionInput: UpdatePaymentInput
|
||||||
): Promise<PaymentProviderSessionResponse["data"]> {
|
): Promise<UpdatePaymentOutput> {
|
||||||
const provider = this.retrieveProvider(providerId)
|
const provider = this.retrieveProvider(providerId)
|
||||||
|
|
||||||
const paymentResponse = await provider.updatePayment(sessionInput)
|
return await provider.updatePayment(sessionInput)
|
||||||
|
|
||||||
if (isPaymentProviderError(paymentResponse)) {
|
|
||||||
this.throwPaymentProviderError(paymentResponse)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (paymentResponse as PaymentProviderSessionResponse)?.data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteSession(input: PaymentProviderDataInput): Promise<void> {
|
async deleteSession(
|
||||||
const provider = this.retrieveProvider(input.provider_id)
|
providerId: string,
|
||||||
|
input: DeletePaymentInput
|
||||||
const error = await provider.deletePayment(input.data)
|
): Promise<DeletePaymentOutput> {
|
||||||
if (isPaymentProviderError(error)) {
|
const provider = this.retrieveProvider(providerId)
|
||||||
this.throwPaymentProviderError(error)
|
return await provider.deletePayment(input)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async authorizePayment(
|
async authorizePayment(
|
||||||
input: PaymentProviderDataInput,
|
providerId: string,
|
||||||
context: Record<string, unknown>
|
input: AuthorizePaymentInput
|
||||||
): Promise<{ data: Record<string, unknown>; status: PaymentSessionStatus }> {
|
): Promise<AuthorizePaymentOutput> {
|
||||||
const provider = this.retrieveProvider(input.provider_id)
|
const provider = this.retrieveProvider(providerId)
|
||||||
|
return await provider.authorizePayment(input)
|
||||||
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(
|
async getStatus(
|
||||||
input: PaymentProviderDataInput
|
providerId: string,
|
||||||
): Promise<PaymentSessionStatus> {
|
input: GetPaymentStatusInput
|
||||||
const provider = this.retrieveProvider(input.provider_id)
|
): Promise<GetPaymentStatusOutput> {
|
||||||
return await provider.getPaymentStatus(input.data)
|
const provider = this.retrieveProvider(providerId)
|
||||||
|
return await provider.getPaymentStatus(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
async capturePayment(
|
async capturePayment(
|
||||||
input: PaymentProviderDataInput
|
providerId: string,
|
||||||
): Promise<Record<string, unknown>> {
|
input: CapturePaymentInput
|
||||||
const provider = this.retrieveProvider(input.provider_id)
|
): Promise<CapturePaymentOutput> {
|
||||||
|
const provider = this.retrieveProvider(providerId)
|
||||||
const res = await provider.capturePayment(input.data)
|
return await provider.capturePayment(input)
|
||||||
if (isPaymentProviderError(res)) {
|
|
||||||
this.throwPaymentProviderError(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
return res as Record<string, unknown>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async cancelPayment(input: PaymentProviderDataInput): Promise<void> {
|
async cancelPayment(
|
||||||
const provider = this.retrieveProvider(input.provider_id)
|
providerId: string,
|
||||||
|
input: CancelPaymentInput
|
||||||
const error = await provider.cancelPayment(input.data)
|
): Promise<CancelPaymentOutput> {
|
||||||
if (isPaymentProviderError(error)) {
|
const provider = this.retrieveProvider(providerId)
|
||||||
this.throwPaymentProviderError(error)
|
return await provider.cancelPayment(input)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async refundPayment(
|
async refundPayment(
|
||||||
input: PaymentProviderDataInput,
|
providerId: string,
|
||||||
amount: BigNumberInput
|
input: RefundPaymentInput
|
||||||
): Promise<Record<string, unknown>> {
|
): Promise<RefundPaymentOutput> {
|
||||||
const provider = this.retrieveProvider(input.provider_id)
|
const provider = this.retrieveProvider(providerId)
|
||||||
|
return await provider.refundPayment(input)
|
||||||
|
}
|
||||||
|
|
||||||
const res = await provider.refundPayment(input.data, amount)
|
async createAccountHolder(
|
||||||
if (isPaymentProviderError(res)) {
|
providerId: string,
|
||||||
this.throwPaymentProviderError(res)
|
input: CreateAccountHolderInput
|
||||||
|
): Promise<CreateAccountHolderOutput> {
|
||||||
|
const provider = this.retrieveProvider(providerId)
|
||||||
|
if (!provider.createAccountHolder) {
|
||||||
|
this.#logger.warn(
|
||||||
|
`Provider ${providerId} does not support creating account holders`
|
||||||
|
)
|
||||||
|
return {} as unknown as CreateAccountHolderOutput
|
||||||
}
|
}
|
||||||
|
|
||||||
return res as Record<string, unknown>
|
return await provider.createAccountHolder(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteAccountHolder(
|
||||||
|
providerId: string,
|
||||||
|
input: DeleteAccountHolderInput
|
||||||
|
): Promise<DeleteAccountHolderOutput> {
|
||||||
|
const provider = this.retrieveProvider(providerId)
|
||||||
|
if (!provider.deleteAccountHolder) {
|
||||||
|
this.#logger.warn(
|
||||||
|
`Provider ${providerId} does not support deleting account holders`
|
||||||
|
)
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await provider.deleteAccountHolder(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
async listPaymentMethods(
|
async listPaymentMethods(
|
||||||
providerId: string,
|
providerId: string,
|
||||||
context: PaymentProviderContext
|
input: ListPaymentMethodsInput
|
||||||
): Promise<PaymentMethodResponse[]> {
|
): Promise<ListPaymentMethodsOutput> {
|
||||||
const provider = this.retrieveProvider(providerId)
|
const provider = this.retrieveProvider(providerId)
|
||||||
if (!provider.listPaymentMethods) {
|
if (!provider.listPaymentMethods) {
|
||||||
throw new MedusaError(
|
this.#logger.warn(
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
`Provider ${providerId} does not support listing payment methods`
|
`Provider ${providerId} does not support listing payment methods`
|
||||||
)
|
)
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return await provider.listPaymentMethods(context)
|
return await provider.listPaymentMethods(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
async savePaymentMethod(
|
async savePaymentMethod(
|
||||||
providerId: string,
|
providerId: string,
|
||||||
input: SavePaymentMethod
|
input: SavePaymentMethodInput
|
||||||
): Promise<SavePaymentMethodResponse> {
|
): Promise<SavePaymentMethodOutput> {
|
||||||
const provider = this.retrieveProvider(providerId)
|
const provider = this.retrieveProvider(providerId)
|
||||||
if (!provider.savePaymentMethod) {
|
if (!provider.savePaymentMethod) {
|
||||||
throw new MedusaError(
|
this.#logger.warn(
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
`Provider ${providerId} does not support saving payment methods`
|
`Provider ${providerId} does not support saving payment methods`
|
||||||
)
|
)
|
||||||
|
return {} as unknown as SavePaymentMethodOutput
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await provider.savePaymentMethod(input)
|
return await provider.savePaymentMethod(input)
|
||||||
|
|
||||||
if (isPaymentProviderError(res)) {
|
|
||||||
this.throwPaymentProviderError(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
return res as SavePaymentMethodResponse
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getWebhookActionAndData(
|
async getWebhookActionAndData(
|
||||||
@@ -198,22 +202,4 @@ Please make sure that the provider is registered in the container and it is conf
|
|||||||
|
|
||||||
return await provider.getWebhookActionAndData(data)
|
return await provider.getWebhookActionAndData(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
private throwPaymentProviderError(errObj: PaymentProviderError) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
`${errObj.error}${errObj.detail ? `:${EOL}${errObj.detail}` : ""}`,
|
|
||||||
errObj.code
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isPaymentProviderError(obj: any): obj is PaymentProviderError {
|
|
||||||
return (
|
|
||||||
obj &&
|
|
||||||
typeof obj === "object" &&
|
|
||||||
"error" in obj &&
|
|
||||||
"code" in obj &&
|
|
||||||
"detail" in obj
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,33 @@
|
|||||||
import Stripe from "stripe"
|
import Stripe from "stripe"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CreatePaymentProviderSession,
|
AuthorizePaymentInput,
|
||||||
PaymentMethodResponse,
|
AuthorizePaymentOutput,
|
||||||
PaymentProviderContext,
|
CancelPaymentInput,
|
||||||
PaymentProviderError,
|
CancelPaymentOutput,
|
||||||
PaymentProviderSessionResponse,
|
CapturePaymentInput,
|
||||||
|
CapturePaymentOutput,
|
||||||
|
CreateAccountHolderInput,
|
||||||
|
CreateAccountHolderOutput,
|
||||||
|
DeleteAccountHolderInput,
|
||||||
|
DeleteAccountHolderOutput,
|
||||||
|
DeletePaymentInput,
|
||||||
|
DeletePaymentOutput,
|
||||||
|
GetPaymentStatusInput,
|
||||||
|
GetPaymentStatusOutput,
|
||||||
|
InitiatePaymentInput,
|
||||||
|
InitiatePaymentOutput,
|
||||||
|
ListPaymentMethodsInput,
|
||||||
|
ListPaymentMethodsOutput,
|
||||||
ProviderWebhookPayload,
|
ProviderWebhookPayload,
|
||||||
SavePaymentMethod,
|
RefundPaymentInput,
|
||||||
SavePaymentMethodResponse,
|
RefundPaymentOutput,
|
||||||
UpdatePaymentProviderSession,
|
RetrievePaymentInput,
|
||||||
|
RetrievePaymentOutput,
|
||||||
|
SavePaymentMethodInput,
|
||||||
|
SavePaymentMethodOutput,
|
||||||
|
UpdatePaymentInput,
|
||||||
|
UpdatePaymentOutput,
|
||||||
WebhookActionResult,
|
WebhookActionResult,
|
||||||
} from "@medusajs/framework/types"
|
} from "@medusajs/framework/types"
|
||||||
import {
|
import {
|
||||||
@@ -95,62 +113,55 @@ abstract class StripeBase extends AbstractPaymentProvider<StripeOptions> {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPaymentStatus(
|
async getPaymentStatus({
|
||||||
paymentSessionData: Record<string, unknown>
|
data,
|
||||||
): Promise<PaymentSessionStatus> {
|
}: GetPaymentStatusInput): Promise<GetPaymentStatusOutput> {
|
||||||
const id = paymentSessionData.id as string
|
const id = data?.id as string
|
||||||
|
if (!id) {
|
||||||
|
throw this.buildError(
|
||||||
|
"No payment intent ID provided while getting payment status",
|
||||||
|
new Error("No payment intent ID provided")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const paymentIntent = await this.stripe_.paymentIntents.retrieve(id)
|
const paymentIntent = await this.stripe_.paymentIntents.retrieve(id)
|
||||||
|
|
||||||
switch (paymentIntent.status) {
|
switch (paymentIntent.status) {
|
||||||
case "requires_payment_method":
|
case "requires_payment_method":
|
||||||
case "requires_confirmation":
|
case "requires_confirmation":
|
||||||
case "processing":
|
case "processing":
|
||||||
return PaymentSessionStatus.PENDING
|
return { status: PaymentSessionStatus.PENDING }
|
||||||
case "requires_action":
|
case "requires_action":
|
||||||
return PaymentSessionStatus.REQUIRES_MORE
|
return { status: PaymentSessionStatus.REQUIRES_MORE }
|
||||||
case "canceled":
|
case "canceled":
|
||||||
return PaymentSessionStatus.CANCELED
|
return { status: PaymentSessionStatus.CANCELED }
|
||||||
case "requires_capture":
|
case "requires_capture":
|
||||||
return PaymentSessionStatus.AUTHORIZED
|
return { status: PaymentSessionStatus.AUTHORIZED }
|
||||||
case "succeeded":
|
case "succeeded":
|
||||||
return PaymentSessionStatus.CAPTURED
|
return { status: PaymentSessionStatus.CAPTURED }
|
||||||
default:
|
default:
|
||||||
return PaymentSessionStatus.PENDING
|
return { status: PaymentSessionStatus.PENDING }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async initiatePayment(
|
async initiatePayment({
|
||||||
input: CreatePaymentProviderSession
|
currency_code,
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse> {
|
amount,
|
||||||
const { email, extra, session_id, customer } = input.context
|
data,
|
||||||
const { currency_code, amount } = input
|
context,
|
||||||
|
}: InitiatePaymentInput): Promise<InitiatePaymentOutput> {
|
||||||
const additionalParameters = this.normalizePaymentIntentParameters(extra)
|
const additionalParameters = this.normalizePaymentIntentParameters(data)
|
||||||
|
|
||||||
const intentRequest: Stripe.PaymentIntentCreateParams = {
|
const intentRequest: Stripe.PaymentIntentCreateParams = {
|
||||||
amount: getSmallestUnit(amount, currency_code),
|
amount: getSmallestUnit(amount, currency_code),
|
||||||
currency: currency_code,
|
currency: currency_code,
|
||||||
metadata: { session_id: session_id! },
|
metadata: { session_id: data?.session_id as string },
|
||||||
...additionalParameters,
|
...additionalParameters,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (customer?.metadata?.stripe_id) {
|
intentRequest.customer = context?.account_holder?.data?.id as
|
||||||
intentRequest.customer = customer.metadata.stripe_id as string
|
| string
|
||||||
} else {
|
| undefined
|
||||||
let stripeCustomer
|
|
||||||
try {
|
|
||||||
stripeCustomer = await this.stripe_.customers.create({
|
|
||||||
email,
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
return this.buildError(
|
|
||||||
"An error occurred in initiatePayment when creating a Stripe customer",
|
|
||||||
e
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
intentRequest.customer = stripeCustomer.id
|
|
||||||
}
|
|
||||||
|
|
||||||
let sessionData
|
let sessionData
|
||||||
try {
|
try {
|
||||||
@@ -158,158 +169,215 @@ abstract class StripeBase extends AbstractPaymentProvider<StripeOptions> {
|
|||||||
intentRequest
|
intentRequest
|
||||||
)) as unknown as Record<string, unknown>
|
)) as unknown as Record<string, unknown>
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return this.buildError(
|
throw this.buildError(
|
||||||
"An error occurred in InitiatePayment during the creation of the stripe payment intent",
|
"An error occurred in InitiatePayment during the creation of the stripe payment intent",
|
||||||
e
|
e
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
id: sessionData.id,
|
||||||
data: sessionData,
|
data: sessionData,
|
||||||
// TODO: REVISIT
|
|
||||||
// update_requests: customer?.metadata?.stripe_id
|
|
||||||
// ? undefined
|
|
||||||
// : {
|
|
||||||
// customer_metadata: {
|
|
||||||
// stripe_id: intentRequest.customer,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async authorizePayment(
|
async authorizePayment(
|
||||||
paymentSessionData: Record<string, unknown>,
|
input: AuthorizePaymentInput
|
||||||
context: Record<string, unknown>
|
): Promise<AuthorizePaymentOutput> {
|
||||||
): Promise<
|
const statusResponse = await this.getPaymentStatus(input)
|
||||||
| PaymentProviderError
|
return statusResponse
|
||||||
| {
|
|
||||||
status: PaymentSessionStatus
|
|
||||||
data: PaymentProviderSessionResponse["data"]
|
|
||||||
}
|
|
||||||
> {
|
|
||||||
const status = await this.getPaymentStatus(paymentSessionData)
|
|
||||||
return { data: paymentSessionData, status }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async cancelPayment(
|
async cancelPayment({
|
||||||
paymentSessionData: Record<string, unknown>
|
data,
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]> {
|
}: CancelPaymentInput): Promise<CancelPaymentOutput> {
|
||||||
try {
|
try {
|
||||||
const id = paymentSessionData.id as string
|
const id = data?.id as string
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return paymentSessionData
|
return { data: data }
|
||||||
}
|
}
|
||||||
|
|
||||||
return (await this.stripe_.paymentIntents.cancel(
|
const res = await this.stripe_.paymentIntents.cancel(id)
|
||||||
id
|
return { data: res as unknown as Record<string, unknown> }
|
||||||
)) as unknown as PaymentProviderSessionResponse["data"]
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.payment_intent?.status === ErrorIntentStatus.CANCELED) {
|
if (error.payment_intent?.status === ErrorIntentStatus.CANCELED) {
|
||||||
return error.payment_intent
|
return { data: error.payment_intent }
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.buildError("An error occurred in cancelPayment", error)
|
throw this.buildError("An error occurred in cancelPayment", error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async capturePayment(
|
async capturePayment({
|
||||||
paymentSessionData: Record<string, unknown>
|
data,
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]> {
|
}: CapturePaymentInput): Promise<CapturePaymentOutput> {
|
||||||
const id = paymentSessionData.id as string
|
const id = data?.id as string
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const intent = await this.stripe_.paymentIntents.capture(id)
|
const intent = await this.stripe_.paymentIntents.capture(id)
|
||||||
return intent as unknown as PaymentProviderSessionResponse["data"]
|
return { data: intent as unknown as Record<string, unknown> }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === ErrorCodes.PAYMENT_INTENT_UNEXPECTED_STATE) {
|
if (error.code === ErrorCodes.PAYMENT_INTENT_UNEXPECTED_STATE) {
|
||||||
if (error.payment_intent?.status === ErrorIntentStatus.SUCCEEDED) {
|
if (error.payment_intent?.status === ErrorIntentStatus.SUCCEEDED) {
|
||||||
return error.payment_intent
|
return { data: error.payment_intent }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.buildError("An error occurred in capturePayment", error)
|
throw this.buildError("An error occurred in capturePayment", error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async deletePayment(
|
async deletePayment(input: DeletePaymentInput): Promise<DeletePaymentOutput> {
|
||||||
paymentSessionData: Record<string, unknown>
|
return await this.cancelPayment(input)
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]> {
|
|
||||||
return await this.cancelPayment(paymentSessionData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async refundPayment(
|
async refundPayment({
|
||||||
paymentSessionData: Record<string, unknown>,
|
amount,
|
||||||
refundAmount: number
|
data,
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]> {
|
}: RefundPaymentInput): Promise<RefundPaymentOutput> {
|
||||||
const id = paymentSessionData.id as string
|
const id = data?.id as string
|
||||||
|
if (!id) {
|
||||||
|
throw this.buildError(
|
||||||
|
"No payment intent ID provided while refunding payment",
|
||||||
|
new Error("No payment intent ID provided")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { currency } = paymentSessionData
|
const currencyCode = data?.currency as string
|
||||||
await this.stripe_.refunds.create({
|
await this.stripe_.refunds.create({
|
||||||
amount: getSmallestUnit(refundAmount, currency as string),
|
amount: getSmallestUnit(amount, currencyCode),
|
||||||
payment_intent: id as string,
|
payment_intent: id as string,
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return this.buildError("An error occurred in refundPayment", e)
|
throw this.buildError("An error occurred in refundPayment", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
return paymentSessionData
|
return { data }
|
||||||
}
|
}
|
||||||
|
|
||||||
async retrievePayment(
|
async retrievePayment({
|
||||||
paymentSessionData: Record<string, unknown>
|
data,
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]> {
|
}: RetrievePaymentInput): Promise<RetrievePaymentOutput> {
|
||||||
try {
|
try {
|
||||||
const id = paymentSessionData.id as string
|
const id = data?.id as string
|
||||||
const intent = await this.stripe_.paymentIntents.retrieve(id)
|
const intent = await this.stripe_.paymentIntents.retrieve(id)
|
||||||
|
|
||||||
intent.amount = getAmountFromSmallestUnit(intent.amount, intent.currency)
|
intent.amount = getAmountFromSmallestUnit(intent.amount, intent.currency)
|
||||||
|
|
||||||
return intent as unknown as PaymentProviderSessionResponse["data"]
|
return { data: intent as unknown as Record<string, unknown> }
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return this.buildError("An error occurred in retrievePayment", e)
|
throw this.buildError("An error occurred in retrievePayment", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updatePayment(
|
async updatePayment({
|
||||||
input: UpdatePaymentProviderSession
|
data,
|
||||||
): Promise<PaymentProviderError | PaymentProviderSessionResponse> {
|
currency_code,
|
||||||
const { context, data, currency_code, amount } = input
|
amount,
|
||||||
|
}: UpdatePaymentInput): Promise<UpdatePaymentOutput> {
|
||||||
const amountNumeric = getSmallestUnit(amount, currency_code)
|
const amountNumeric = getSmallestUnit(amount, currency_code)
|
||||||
|
if (isPresent(amount) && data?.amount === amountNumeric) {
|
||||||
|
return { data }
|
||||||
|
}
|
||||||
|
|
||||||
const stripeId = context.customer?.metadata?.stripe_id
|
try {
|
||||||
|
const id = data?.id as string
|
||||||
|
const sessionData = (await this.stripe_.paymentIntents.update(id, {
|
||||||
|
amount: amountNumeric,
|
||||||
|
})) as unknown as Record<string, unknown>
|
||||||
|
|
||||||
if (stripeId !== data.customer) {
|
return { data: sessionData }
|
||||||
return await this.initiatePayment(input)
|
} catch (e) {
|
||||||
} else {
|
throw this.buildError("An error occurred in updatePayment", e)
|
||||||
if (isPresent(amount) && data.amount === amountNumeric) {
|
|
||||||
return { data }
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const id = data.id as string
|
|
||||||
const sessionData = (await this.stripe_.paymentIntents.update(id, {
|
|
||||||
amount: amountNumeric,
|
|
||||||
})) as unknown as PaymentProviderSessionResponse["data"]
|
|
||||||
|
|
||||||
return { data: sessionData }
|
|
||||||
} catch (e) {
|
|
||||||
return this.buildError("An error occurred in updatePayment", e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async listPaymentMethods(
|
async createAccountHolder({
|
||||||
context: PaymentProviderContext
|
context,
|
||||||
): Promise<PaymentMethodResponse[]> {
|
}: CreateAccountHolderInput): Promise<CreateAccountHolderOutput> {
|
||||||
const customerId = context.customer?.metadata?.stripe_id
|
const { account_holder, customer } = context
|
||||||
if (!customerId) {
|
|
||||||
|
if (account_holder?.data?.id) {
|
||||||
|
return { id: account_holder.data.id as string }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!customer) {
|
||||||
|
throw this.buildError(
|
||||||
|
"No customer in context",
|
||||||
|
new Error("No customer provided while creating account holder")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const shipping = customer.billing_address
|
||||||
|
? ({
|
||||||
|
address: {
|
||||||
|
city: customer.billing_address.city,
|
||||||
|
country: customer.billing_address.country_code,
|
||||||
|
line1: customer.billing_address.address_1,
|
||||||
|
line2: customer.billing_address.address_2,
|
||||||
|
postal_code: customer.billing_address.postal_code,
|
||||||
|
state: customer.billing_address.province,
|
||||||
|
},
|
||||||
|
} as Stripe.CustomerCreateParams.Shipping)
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
try {
|
||||||
|
const stripeCustomer = await this.stripe_.customers.create({
|
||||||
|
email: customer.email,
|
||||||
|
name:
|
||||||
|
customer.company_name ||
|
||||||
|
`${customer.first_name ?? ""} ${customer.last_name ?? ""}`.trim() ||
|
||||||
|
undefined,
|
||||||
|
phone: customer.phone as string | undefined,
|
||||||
|
...shipping,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: stripeCustomer.id,
|
||||||
|
data: stripeCustomer as unknown as Record<string, unknown>,
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
throw this.buildError(
|
||||||
|
"An error occurred in createAccountHolder when creating a Stripe customer",
|
||||||
|
e
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteAccountHolder({
|
||||||
|
context,
|
||||||
|
}: DeleteAccountHolderInput): Promise<DeleteAccountHolderOutput> {
|
||||||
|
const { account_holder } = context
|
||||||
|
const accountHolderId = account_holder?.data?.id as string | undefined
|
||||||
|
if (!accountHolderId) {
|
||||||
|
throw this.buildError(
|
||||||
|
"No account holder in context",
|
||||||
|
new Error("No account holder provided while deleting account holder")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.stripe_.customers.del(accountHolderId)
|
||||||
|
return {}
|
||||||
|
} catch (e) {
|
||||||
|
throw this.buildError("An error occurred in deleteAccountHolder", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async listPaymentMethods({
|
||||||
|
context,
|
||||||
|
}: ListPaymentMethodsInput): Promise<ListPaymentMethodsOutput> {
|
||||||
|
const accountHolderId = context?.account_holder?.data?.id as
|
||||||
|
| string
|
||||||
|
| undefined
|
||||||
|
if (!accountHolderId) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
const paymentMethods = await this.stripe_.customers.listPaymentMethods(
|
const paymentMethods = await this.stripe_.customers.listPaymentMethods(
|
||||||
customerId as string,
|
accountHolderId,
|
||||||
// In order to keep the interface simple, we just list the maximum payment methods, which should be enough in almost all cases.
|
// In order to keep the interface simple, we just list the maximum payment methods, which should be enough in almost all cases.
|
||||||
// We can always extend the interface to allow additional filtering, if necessary.
|
// We can always extend the interface to allow additional filtering, if necessary.
|
||||||
{ limit: 100 }
|
{ limit: 100 }
|
||||||
@@ -321,21 +389,23 @@ abstract class StripeBase extends AbstractPaymentProvider<StripeOptions> {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
async savePaymentMethod(
|
async savePaymentMethod({
|
||||||
input: SavePaymentMethod
|
context,
|
||||||
): Promise<PaymentProviderError | SavePaymentMethodResponse> {
|
data,
|
||||||
const { context, data } = input
|
}: SavePaymentMethodInput): Promise<SavePaymentMethodOutput> {
|
||||||
const customer = context?.customer
|
const accountHolderId = context?.account_holder?.data?.id as
|
||||||
|
| string
|
||||||
|
| undefined
|
||||||
|
|
||||||
if (!customer?.metadata?.stripe_id) {
|
if (!accountHolderId) {
|
||||||
return this.buildError(
|
throw this.buildError(
|
||||||
"Account holder not set while saving a payment method",
|
"Account holder not set while saving a payment method",
|
||||||
new Error("Missing account holder")
|
new Error("Missing account holder")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const resp = await this.stripe_.setupIntents.create({
|
const resp = await this.stripe_.setupIntents.create({
|
||||||
customer: customer.metadata.stripe_id as string,
|
customer: accountHolderId,
|
||||||
...data,
|
...data,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -397,18 +467,15 @@ abstract class StripeBase extends AbstractPaymentProvider<StripeOptions> {
|
|||||||
this.options_.webhookSecret
|
this.options_.webhookSecret
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
protected buildError(message: string, error: Error): PaymentProviderError {
|
protected buildError(message: string, error: Error): Error {
|
||||||
const errorDetails =
|
const errorDetails =
|
||||||
"raw" in error ? (error.raw as Stripe.StripeRawError) : error
|
"raw" in error ? (error.raw as Stripe.StripeRawError) : error
|
||||||
|
|
||||||
return {
|
return new Error(
|
||||||
error: `${message}: ${error.message}`,
|
`${message}: ${error.message}. ${
|
||||||
code: "code" in errorDetails ? errorDetails.code : "unknown",
|
"detail" in errorDetails ? errorDetails.detail : ""
|
||||||
detail:
|
}`.trim()
|
||||||
"detail" in errorDetails
|
)
|
||||||
? `${error.message}: ${errorDetails.detail}`
|
|
||||||
: error.message,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user