diff --git a/www/apps/resources/app/commerce-modules/customer/links-to-other-modules/page.mdx b/www/apps/resources/app/commerce-modules/customer/links-to-other-modules/page.mdx index bf91bf2576..67657e8069 100644 --- a/www/apps/resources/app/commerce-modules/customer/links-to-other-modules/page.mdx +++ b/www/apps/resources/app/commerce-modules/customer/links-to-other-modules/page.mdx @@ -18,11 +18,106 @@ Read-only links are used to query data across modules, but the relations aren't +- [`Customer` data model \<\> `AccountHolder` data model of Payment Module](#payment-module). - [`Cart` data model of Cart Module \<\> `Customer` data model](#cart-module). (Read-only). - [`Order` data model of Order Module \<\> `Customer` data model](#order-module). (Read-only). --- +## Payment Module + +Medusa defines a link between the `Customer` and `AccountHolder` data models, allowing payment providers to save payment methods for a customer, if the payment provider supports it. + + + +This link is available starting from Medusa `v2.5.0`. + + + +### Retrieve with Query + +To retrieve the account holder associated with a customer with [Query](!docs!/learn/fundamentals/module-links/query), pass `customer.*` in `fields`: + + + + +```ts +const { data: customers } = await query.graph({ + entity: "customer", + fields: [ + "account_holder.*", + ], +}) + +// customers.account_holder +``` + + + + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: customers } = useQueryGraphStep({ + entity: "customer", + fields: [ + "account_holder.*", + ], +}) + +// customers.account_holder +``` + + + + +### Manage with Link + +To manage the account holders of a customer, use [Link](!docs!/learn/fundamentals/module-links/link): + + + + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.create({ + [Modules.CUSTOMER]: { + customer_id: "cus_123", + }, + [Modules.PAYMENT]: { + account_holder_id: "acchld_123", + }, +}) +``` + + + + +```ts +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" + +// ... + +createRemoteLinkStep({ + [Modules.CUSTOMER]: { + customer_id: "cus_123", + }, + [Modules.PAYMENT]: { + account_holder_id: "acchld_123", + }, +}) +``` + + + + +--- + ## Cart Module Medusa defines a read-only link between the `Customer` data model and the [Cart Module](../../cart/page.mdx)'s `Cart` data model. This means you can retrieve the details of a customer's carts, but you don't manage the links in a pivot table in the database. The customer of a cart is determined by the `customer_id` property of the `Cart` data model. diff --git a/www/apps/resources/app/commerce-modules/payment/account-holder/page.mdx b/www/apps/resources/app/commerce-modules/payment/account-holder/page.mdx new file mode 100644 index 0000000000..34de83ba80 --- /dev/null +++ b/www/apps/resources/app/commerce-modules/payment/account-holder/page.mdx @@ -0,0 +1,47 @@ +export const metadata = { + title: `Account Holders and Saved Payment Methods`, +} + +# {metadata.title} + +In this documentation, you'll learn about account holders, and how they're used to save payment methods in third-party payment providers. + + + +Account holders are available starting from Medusa `v2.5.0`. + + + +## What's an Account Holder? + +An account holder represents a customer that can have saved payment methods in a third-party service. It's represented by the `AccountHolder` data model. + +It holds fields retrieved from the third-party provider, such as: + +- `external_id`: The ID of the equivalent customer or account holder in the third-party provider. +- `data`: Data returned by the payment provider when the account holder is created. + +A payment provider that supports saving payment methods for customers would create the equivalent of an account holder in the third-party provider. Then, whenever a payment method is saved, it would be saved under the account holder in the third-party provider. + +--- + +## Save Payment Methods + +If a payment provider supports saving payment methods for a customer, they must implement the following methods: + +- `createAccountHolder`: Creates an account holder in the payment provider. The Payment Module uses this method before creating the account holder in Medusa, and uses the returned data to set fields like `external_id` and `data` in the created `AccountHolder` record. +- `deleteAccountHolder`: Deletes an account holder in the payment provider. The Payment Module uses this method when an account holder is deleted in Medusa. +- `savePaymentMethod`: Saves a payment method for an account holder in the payment provider. +- `listPaymentMethods`: Lists saved payment methods in the third-party service for an account holder. This is useful when displaying the customer's saved payment methods in the storefront. + +Learn more about implementing these methods in the [Create Payment Provider guide](/references/payment/provider). + +--- + +## Account Holder in Medusa Payment Flows + +In the Medusa application, when a payment session is created for a registered customer, the Medusa application uses the Payment Module to create an account holder for the customer. + +Consequently, the Payment Module uses the payment provider to create an account holder in the third-party service, then creates the account holder in Medusa. + +This flow is only supported if the chosen payment provider has implemented the necessary [save payment methods](#save-payment-methods). diff --git a/www/apps/resources/app/commerce-modules/payment/links-to-other-modules/page.mdx b/www/apps/resources/app/commerce-modules/payment/links-to-other-modules/page.mdx index 27c4d17f2e..649f423133 100644 --- a/www/apps/resources/app/commerce-modules/payment/links-to-other-modules/page.mdx +++ b/www/apps/resources/app/commerce-modules/payment/links-to-other-modules/page.mdx @@ -13,6 +13,7 @@ This document showcases the module links defined between the Payment Module and The Payment Module has the following links to other modules: - [`Cart` data model of Cart Module \<\> `PaymentCollection` data model](#cart-module). +- [`Customer` data model of Customer Module \<\> `AccountHolder` data model](#customer-module). - [`Order` data model of Order Module \<\> `PaymentCollection` data model](#order-module). - [`OrderClaim` data model of Order Module \<\> `PaymentCollection` data model](#order-module). - [`OrderExchange` data model of Order Module \<\> `PaymentCollection` data model](#order-module). @@ -112,6 +113,100 @@ createRemoteLinkStep({ --- +## Customer Module + +Medusa defines a link between the `Customer` and `AccountHolder` data models, allowing payment providers to save payment methods for a customer, if the payment provider supports it. + + + +This link is available starting from Medusa `v2.5.0`. + + + +### Retrieve with Query + +To retrieve the customer associated with an account holder with [Query](!docs!/learn/fundamentals/module-links/query), pass `customer.*` in `fields`: + + + + +```ts +const { data: accountHolders } = await query.graph({ + entity: "account_holder", + fields: [ + "customer.*", + ], +}) + +// accountHolders.customer +``` + + + + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: accountHolders } = useQueryGraphStep({ + entity: "account_holder", + fields: [ + "customer.*", + ], +}) + +// accountHolders.customer +``` + + + + +### Manage with Link + +To manage the account holders of a customer, use [Link](!docs!/learn/fundamentals/module-links/link): + + + + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.create({ + [Modules.CUSTOMER]: { + customer_id: "cus_123", + }, + [Modules.PAYMENT]: { + account_holder_id: "acchld_123", + }, +}) +``` + + + + +```ts +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" + +// ... + +createRemoteLinkStep({ + [Modules.CUSTOMER]: { + customer_id: "cus_123", + }, + [Modules.PAYMENT]: { + account_holder_id: "acchld_123", + }, +}) +``` + + + + +--- + ## Order Module An order's payment details are stored in a payment collection. This also applies for claims and exchanges. diff --git a/www/apps/resources/app/commerce-modules/payment/page.mdx b/www/apps/resources/app/commerce-modules/payment/page.mdx index 467729bcad..109737f848 100644 --- a/www/apps/resources/app/commerce-modules/payment/page.mdx +++ b/www/apps/resources/app/commerce-modules/payment/page.mdx @@ -21,6 +21,7 @@ Learn more about why modules are isolated in [this documentation](!docs!/learn/f - [Authorize, Capture, and Refund Payments](./payment/page.mdx): Authorize, capture, and refund payments for a single resource. - [Payment Collection Management](./payment-collection/page.mdx): Store and manage all payments of a single resources, such as a cart, in payment collections. - [Integrate Third-Party Payment Providers](./payment-provider/page.mdx): Use payment providers like [Stripe](./payment-provider/stripe/page.mdx) to handle and process payments, or integrate custom payment providers. +- [Saved Payment Methods](./account-holder/page.mdx): Save payment methods for customers in third-party payment providers. - [Handle Webhook Events](./webhook-events/page.mdx): Handle webhook events from third-party providers and process the associated payment. --- diff --git a/www/apps/resources/generated/edit-dates.mjs b/www/apps/resources/generated/edit-dates.mjs index db3a93be58..f0cb2ce3ed 100644 --- a/www/apps/resources/generated/edit-dates.mjs +++ b/www/apps/resources/generated/edit-dates.mjs @@ -52,7 +52,7 @@ export const generatedEditDates = { "app/commerce-modules/payment/payment-provider/page.mdx": "2024-10-09T11:07:27.269Z", "app/commerce-modules/payment/payment-session/page.mdx": "2024-10-09T10:58:00.960Z", "app/commerce-modules/payment/webhook-events/page.mdx": "2024-11-19T11:45:02.167Z", - "app/commerce-modules/payment/page.mdx": "2025-01-16T10:33:31.791Z", + "app/commerce-modules/payment/page.mdx": "2025-01-31T09:38:17.917Z", "app/commerce-modules/pricing/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00", "app/commerce-modules/pricing/_events/page.mdx": "2024-07-03T19:27:13+03:00", "app/commerce-modules/pricing/concepts/page.mdx": "2024-10-09T13:37:25.678Z", @@ -2124,7 +2124,7 @@ export const generatedEditDates = { "app/commerce-modules/order/edit/page.mdx": "2024-10-09T08:50:05.334Z", "app/commerce-modules/order/links-to-other-modules/page.mdx": "2025-01-06T11:19:35.604Z", "app/commerce-modules/order/order-change/page.mdx": "2024-10-09T09:59:40.745Z", - "app/commerce-modules/payment/links-to-other-modules/page.mdx": "2025-01-06T11:19:35.593Z", + "app/commerce-modules/payment/links-to-other-modules/page.mdx": "2025-01-31T09:22:05.326Z", "references/core_flows/Common/Steps_Common/functions/core_flows.Common.Steps_Common.useQueryGraphStep/page.mdx": "2025-01-13T17:30:23.157Z", "references/core_flows/Payment/Workflows_Payment/functions/core_flows.Payment.Workflows_Payment.processPaymentWorkflow/page.mdx": "2025-01-27T11:43:50.940Z", "references/helper_steps/functions/helper_steps.useQueryGraphStep/page.mdx": "2025-01-17T16:43:22.242Z", @@ -5737,7 +5737,7 @@ export const generatedEditDates = { "app/commerce-modules/tax/admin-widget-zones/page.mdx": "2024-12-24T08:47:13.176Z", "app/commerce-modules/user/admin-widget-zones/page.mdx": "2024-12-24T08:48:14.186Z", "app/commerce-modules/currency/links-to-other-modules/page.mdx": "2024-12-24T14:47:10.556Z", - "app/commerce-modules/customer/links-to-other-modules/page.mdx": "2024-12-24T14:48:54.689Z", + "app/commerce-modules/customer/links-to-other-modules/page.mdx": "2025-01-31T09:26:54.541Z", "app/commerce-modules/fulfillment/events/page.mdx": "2024-12-31T09:37:49.253Z", "app/commerce-modules/payment/events/page.mdx": "2024-12-31T09:41:56.582Z", "references/core_flows/Payment/Steps_Payment/functions/core_flows.Payment.Steps_Payment.refundPaymentsStep/page.mdx": "2025-01-27T11:43:50.929Z", @@ -5902,6 +5902,7 @@ export const generatedEditDates = { "references/types/HttpTypes/interfaces/types.HttpTypes.StoreProductTypeResponse/page.mdx": "2025-01-27T11:43:54.212Z", "references/types/interfaces/types.BaseProductTypeListParams/page.mdx": "2025-01-27T11:43:54.550Z", "references/core_flows/Order/Steps_Order/variables/core_flows.Order.Steps_Order.updateOrderChangesStepId/page.mdx": "2025-01-27T11:43:49.278Z", + "app/commerce-modules/payment/account-holder/page.mdx": "2025-01-31T09:37:41.595Z", "app/troubleshooting/test-errors/page.mdx": "2025-01-31T13:08:42.639Z", "app/commerce-modules/product/variant-inventory/page.mdx": "2025-02-03T12:19:45.706Z", "app/examples/guides/custom-item-price/page.mdx": "2025-02-07T09:21:11.170Z" diff --git a/www/apps/resources/generated/files-map.mjs b/www/apps/resources/generated/files-map.mjs index ec4c7687b8..14ac97c4f3 100644 --- a/www/apps/resources/generated/files-map.mjs +++ b/www/apps/resources/generated/files-map.mjs @@ -431,6 +431,10 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/page.mdx", "pathname": "/commerce-modules" }, + { + "filePath": "/www/apps/resources/app/commerce-modules/payment/account-holder/page.mdx", + "pathname": "/commerce-modules/payment/account-holder" + }, { "filePath": "/www/apps/resources/app/commerce-modules/payment/events/page.mdx", "pathname": "/commerce-modules/payment/events" diff --git a/www/apps/resources/generated/sidebar.mjs b/www/apps/resources/generated/sidebar.mjs index 74f1d31c2f..6e29237134 100644 --- a/www/apps/resources/generated/sidebar.mjs +++ b/www/apps/resources/generated/sidebar.mjs @@ -8369,6 +8369,14 @@ export const generatedSidebar = [ "title": "Payment Provider Module", "children": [] }, + { + "loaded": true, + "isPathHref": true, + "type": "link", + "path": "/commerce-modules/payment/account-holder", + "title": "Account Holder", + "children": [] + }, { "loaded": true, "isPathHref": true, diff --git a/www/apps/resources/sidebars/payment.mjs b/www/apps/resources/sidebars/payment.mjs index 867fa3c6d4..5ea32df1c0 100644 --- a/www/apps/resources/sidebars/payment.mjs +++ b/www/apps/resources/sidebars/payment.mjs @@ -43,6 +43,11 @@ export const paymentSidebar = [ path: "/commerce-modules/payment/payment-provider", title: "Payment Provider Module", }, + { + type: "link", + path: "/commerce-modules/payment/account-holder", + title: "Account Holder", + }, { type: "link", path: "/commerce-modules/payment/webhook-events",