docs: publish restructure (#3496)

* docs: added features and guides overview page

* added image

* added version 2

* added version 3

* added version 4

* docs: implemented new color scheme

* docs: redesigned sidebar (#3193)

* docs: redesigned navbar for restructure (#3199)

* docs: redesigned footer (#3209)

* docs: redesigned cards (#3230)

* docs: redesigned admonitions (#3231)

* docs: redesign announcement bar (#3236)

* docs: redesigned large cards (#3239)

* docs: redesigned code blocks (#3253)

* docs: redesigned search modal and page (#3264)

* docs: redesigned doc footer (#3268)

* docs: added new sidebars + refactored css and assets (#3279)

* docs: redesigned api reference sidebar

* docs: refactored css

* docs: added code tabs transition

* docs: added new sidebars

* removed unused assets

* remove unusued assets

* Fix deploy errors

* fix incorrect link

* docs: fixed code responsivity + missing icons (#3283)

* docs: changed icons (#3296)

* docs: design fixes to the sidebar (#3297)

* redesign fixes

* docs: small design fixes

* docs: several design fixes after restructure (#3299)

* docs: bordered icon fixes

* docs: desgin fixes

* fixes to code blocks and sidebar scroll

* design adjustments

* docs: restructured homepage (#3305)

* docs: restructured homepage

* design fixes

* fixed core concepts icon

* docs: added core concepts page (#3318)

* docs: restructured homepage

* design fixes

* docs: added core concepts page

* changed text of different components

* docs: added architecture link

* added missing prop for user guide

* docs: added regions overview page (#3327)

* docs: added regions overview

* moved region pages to new structure

* docs: fixed description of regions architecture page

* small changes

* small fix

* docs: added customers overview page (#3331)

* docs: added regions overview

* moved region pages to new structure

* docs: fixed description of regions architecture page

* small changes

* small fix

* docs: added customers overview page

* fix link

* resolve link issues

* docs: updated regions architecture image

* docs: second-iteration fixes (#3347)

* docs: redesigned document

* design fixes

* docs: added products overview page (#3354)

* docs: added carts overview page (#3363)

* docs: added orders overview (#3364)

* docs: added orders overview

* added links in overview

* docs: added vercel redirects

* docs: added soon badge for cards (#3389)

* docs: resolved feedback changes + organized troubleshooting pages (#3409)

* docs: resolved feedback changes

* added extra line

* docs: changed icons for restructure (#3421)

* docs: added taxes overview page (#3422)

* docs: added taxes overview page

* docs: fix sidebar label

* added link to taxes overview page

* fixed link

* docs: fixed sidebar scroll (#3429)

* docs: added discounts overview (#3432)

* docs: added discounts overview

* fixed links

* docs: added gift cards overview (#3433)

* docs: added price lists overview page (#3440)

* docs: added price lists overview page

* fixed links

* docs: added sales channels overview page (#3441)

* docs: added sales overview page

* fixed links

* docs: added users overview (#3443)

* docs: fixed sidebar border height (#3444)

* docs: fixed sidebar border height

* fixed svg markup

* docs: added possible solutions to feedback component (#3449)

* docs: added several overview pages + restructured files (#3463)

* docs: added several overview pages

* fixed links

* docs: added feature flags + PAK overview pages (#3464)

* docs: added feature flags + PAK overview pages

* fixed links

* fix link

* fix link

* fixed links colors

* docs: added strategies overview page (#3468)

* docs: automated upgrade guide (#3470)

* docs: automated upgrade guide

* fixed vercel redirect

* docs: restructured files in docs codebase (#3475)

* docs: restructured files

* docs: fixed eslint exception

* docs: finished restructure loose-ends (#3493)

* fixed uses of backend

* docs: finished loose ends

* eslint fixes

* fixed links

* merged master

* added update instructions for v1.7.12
This commit is contained in:
Shahed Nasser
2023-03-16 17:03:10 +02:00
committed by GitHub
parent f312ce1e0f
commit 1decaa27c7
415 changed files with 12422 additions and 5098 deletions
@@ -0,0 +1,313 @@
---
description: 'Learn how to create a fulfillment provider in the Medusa backend. This guide explains the different methods in the fulfillment provider.'
addHowToData: true
---
# How to Add a Fulfillment Provider
In this document, youll learn how to add a fulfillment provider to a Medusa backend. If youre unfamiliar with the Shipping architecture in Medusa, make sure to [check out the overview first](../shipping.md).
## Overview
A fulfillment provider is the shipping provider used to fulfill orders and deliver them to customers. An example of a fulfillment provider is FedEx.
By default, a Medusa Backend has a `manual` fulfillment provider which has minimal implementation. It allows you to accept orders and fulfill them manually. However, you can integrate any fulfillment provider into Medusa, and your fulfillment provider can interact with third-party shipping providers.
Adding a fulfillment provider is as simple as creating one [service](../../../development/services/create-service.md) file in `src/services`. A fulfillment provider is essentially a service that extends the `FulfillmentService`. It requires implementing 4 methods:
1. `getFulfillmentOptions`: used to retrieve available fulfillment options provided by this fulfillment provider.
2. `validateOption`: used to validate the shipping option when its being created by the admin.
3. `validateFulfillmentData`: used to validate the shipping method when the customer chooses a shipping option on checkout.
4. `createFulfillment`: used to perform any additional actions when fulfillment is being created for an order.
Also, the fulfillment provider class should have a static property `identifier`. It is the name that will be used to install and refer to the fulfillment provider throughout Medusa.
Fulfillment providers are loaded and installed on the backend startup.
---
## Create a Fulfillment Provider
The first step is to create a JavaScript or TypeScript file under `src/services`. For example, create the file `src/services/my-fulfillment.ts` with the following content:
```ts title=src/services/my-fulfillment.ts
import { FulfillmentService } from "medusa-interfaces"
class MyFulfillmentService extends FulfillmentService {
}
export default MyFulfillmentService
```
Fulfillment provider services must extend the `FulfillmentService` class imported from `medusa-interfaces`.
:::note
Following the naming convention of Services, the name of the file should be the slug name of the fulfillment provider, and the name of the class should be the camel case name of the fulfillment provider suffixed with “Service”. You can learn more in the [service documentation](../../../development/services/create-service.md).
:::
### Identifier
As mentioned in the overview, fulfillment providers should have a static `identifier` property.
The `FulfillmentProvider` entity has 2 properties: `identifier` and `is_installed`. The `identifier` property in the class will be used when the fulfillment provider is created in the database.
The value of this property will also be used to reference the fulfillment provider throughout Medusa. For example, it is used to [add a fulfillment provider](/api/admin/#tag/Region/operation/PostRegionsRegionFulfillmentProviders) to a region.
```ts
import { FulfillmentService } from "medusa-interfaces"
class MyFulfillmentService extends FulfillmentService {
static identifier = "my-fulfillment"
}
export default MyFulfillmentService
```
### constructor
You can use the `constructor` of your fulfillment provider to have access to different services in Medusa through dependency injection. You can access any services you create in Medusa in the first parameter.
You can also use the constructor to initialize your integration with the third-party provider. For example, if you use a client to connect to the third-party providers APIs, you can initialize it in the constructor and use it in other methods in the service.
Additionally, if youre creating your fulfillment provider as an external plugin to be installed on any Medusa backend and you want to access the options added for the plugin, you can access it in the constructor. The options are passed as a second parameter.
For example:
```ts
class MyFulfillmentService extends FulfillmentService {
// ...
constructor(container, options) {
super(container)
// you can access options here
}
}
```
### getFulfillmentOptions
When the admin is creating shipping options available for customers during checkout, they choose one of the fulfillment options provided by underlying fulfillment providers.
For example, if youre integrating UPS as a fulfillment provider, you might support two fulfillment options: UPS Express Shipping and UPS Access Point.
These fulfillment options are defined in the `getFulfillmentOptions` method. This method should return an array of options.
For example:
```ts
class MyFulfillmentService extends FulfillmentService {
// ...
async getFulfillmentOptions() {
return [
{
id: "my-fulfillment",
},
{
id: "my-fulfillment-dynamic",
},
]
}
}
```
When the admin chooses one of those fulfillment options, the data of the chosen fulfillment option is stored in the `data` property of the shipping option created. This property is used to add any additional data you need to fulfill the order with the third-party provider.
For that reason, the fulfillment option doesn't have any required structure and can be of any format that works for your integration.
### validateOption
Once the admin creates the shipping option, the data will be validated first using this method in the underlying fulfillment provider of that shipping option. This method is called when a `POST` request is sent to [`/admin/shipping-options`](/api/admin/#tag/Shipping-Option/operation/PostShippingOptions).
This method accepts the `data` object that is sent in the body of the request. You can use this data to validate the shipping option before it is saved.
This method returns a boolean. If the result is false, an error is thrown and the shipping option will not be saved.
For example, you can use this method to ensure that the `id` in the `data` object is correct:
```ts
class MyFulfillmentService extends FulfillmentService {
// ...
async validateOption(data) {
return data.id == "my-fulfillment"
}
}
```
If your fulfillment provider does not need to run any validation, you can simply return `true`.
### validateFulfillmentOption
When the customer chooses a shipping option on checkout, the shipping option and its data are validated before the shipping method is created.
`validateFulfillmentOption` is called when a `POST` request is sent to [`/carts/:id/shipping-methods`](/api/store/#tag/Cart/operation/PostCartsCartShippingMethod).
This method accepts three parameters:
1. The shipping option data.
2. The `data` object passed in the body of the request.
3. The customers cart data.
You can use these parameters to validate the chosen shipping option. For example, you can check if the `data` object includes all data needed to fulfill the shipment later on.
If any of the data is invalid, you can throw an error. This error will stop Medusa from creating a shipping method and the error message will be returned as a result to the endpoint.
If everything is valid, this method must return a value that will be stored in the `data` property of the shipping method to be created. So, make sure the value you return contains everything you need to fulfill the shipment later on.
For example:
```ts
class MyFulfillmentService extends FulfillmentService {
// ...
async validateFulfillmentData(optionData, data, cart) {
if (data.id !== "my-fulfillment") {
throw new Error("invalid data")
}
return {
...data,
}
}
}
```
### createFulfillment
After an order is placed, it can be fulfilled either manually by the admin or using automation.
This method gives you access to the fulfillment being created as well as other details in case you need to perform any additional actions with the third-party provider.
This method accepts four parameters:
1. The data of the shipping method associated with the order.
2. An array of items in the order to be fulfilled. The admin can choose all or some of the items to fulfill.
3. The data of the order
4. The data of the fulfillment being created.
You can use the `data` property in the shipping method (first parameter) to access the data specific to the shipping option. This is based on your implementation of previous methods.
Here is a basic implementation of `createFulfillment` for a fulfillment provider that does not interact with any third-party provider to create the fulfillment:
```ts
class MyFulfillmentService extends FulfillmentService {
// ...
createFulfillment(
methodData,
fulfillmentItems,
fromOrder,
fulfillment
) {
// No data is being sent anywhere
return Promise.resolve({})
}
}
```
:::note
This method is also used to create claims and swaps. The fulfillment object has the fields `claim_id`, `swap_id`, and `order_id`. You can check which isnt null to determine what type of fulfillment is being created.
:::
### Useful Methods
The above-detailed methods are the required methods for every fulfillment provider. However, there are additional methods that you can use in your fulfillment provider to customize it further or add additional features.
#### canCalculate
This method validates whether a shipping option is calculated dynamically or flat rate. It is called if the `price_type` of the shipping option being created is set to `calculated`.
If this method returns `true`, that means that the price should be calculated dynamically. The `amount` property of the shipping option will then be set to `null`. The amount will be created later when the shipping method is created on checkout using the `calculatePrice` method (explained next).
If the method returns `false`, an error is thrown as it means the selected shipping option can only be chosen if the price type is set to `flat_rate`.
This method receives as a parameter the `data` object sent with the request that [creates the shipping option.](/api/admin/#tag/Shipping-Option/operation/PostShippingOptions) You can use this data to determine whether the shipping option should be calculated or not. This is useful if the fulfillment provider you are integrating has both flat rate and dynamically priced fulfillment options.
For example:
```ts
class MyFulfillmentService extends FulfillmentService {
// ...
canCalculate(data) {
return data.id === "my-fulfillment-dynamic"
}
}
```
#### calculatePrice
This method is called on checkout when the shipping method is being created if the `price_type` of the selected shipping option is `calculated`.
This method receives three parameters:
1. The `data` parameter of the selected shipping option.
2. The `data` parameter sent with [the request](/api/store/#tag/Cart/operation/PostCartsCartShippingMethod).
3. The customers cart data.
If your fulfillment provider does not provide any dynamically calculated rates you can keep the function empty:
```ts
class MyFulfillmentService extends FulfillmentService {
// ...
calculatePrice() {
// leave empty
}
}
```
Otherwise, you can use it to calculate the price with a custom logic. For example:
```ts
class MyFulfillmentService extends FulfillmentService {
// ...
calculatePrice(optionData, data, cart) {
return cart.items.length * 1000
}
}
```
#### createReturn
Fulfillment providers can also be used to return products. A shipping option can be used for returns if the `is_return` property is `true` or if an admin creates a Return Shipping Option from the settings.
This method is called when the admin [creates a return request](/api/admin/#tag/Order/operation/PostOrdersOrderReturns) for an order or when the customer [creates a return of their order](/api/store/#tag/Return/operation/PostReturns).
It gives you access to the return being created in case you need to perform any additional actions with the third-party provider.
It receives the return created as a parameter. The value it returns is set to the `shipping_data` of the return instance.
This is the basic implementation of the method for a fulfillment provider that does not contact with a third-party provider to fulfill the return:
```ts
class MyFulfillmentService extends FulfillmentService {
// ...
createReturn(returnOrder) {
return Promise.resolve({})
}
}
```
#### cancelFulfillment
This method is called when a fulfillment is cancelled by the admin. This fulfillment can be for an order, a claim, or a swap.
It gives you access to the fulfillment being canceled in case you need to perform any additional actions with your third-party provider.
This method receives the fulfillment being cancelled as a parameter.
This is the basic implementation of the method for a fulfillment provider that does not interact with a third-party provider to cancel the fulfillment:
```ts
class MyFulfillmentService extends FulfillmentService {
// ...
cancelFulfillment(fulfillment) {
return Promise.resolve({})
}
}
```
---
## See Also
- Example Implementations: [Webshipper plugin](https://github.com/medusajs/medusa/tree/cab5821f55cfa448c575a20250c918b7fc6835c9/packages/medusa-fulfillment-webshipper) and the [manual fulfillment plugin](https://github.com/medusajs/medusa/tree/cab5821f55cfa448c575a20250c918b7fc6835c9/packages/medusa-fulfillment-manual)
@@ -0,0 +1,654 @@
---
description: 'Learn how to create a payment provider in the Medusa backend. This guide explains the different methods available in a fulfillment provider.'
addHowToData: true
---
# How to Create a Payment Provider
In this document, youll learn how to add a Payment Provider to your Medusa backend. If youre unfamiliar with the Payment architecture in Medusa, make sure to check out the [overview](../payment.md) first.
## Overview
A Payment Provider is the payment method used to authorize, capture, and refund payment, among other actions. An example of a Payment Provider is Stripe.
By default, Medusa has a [manual payment provider](https://github.com/medusajs/medusa/tree/master/packages/medusa-payment-manual) that has minimal implementation. It can be synonymous with a Cash on Delivery payment method. It allows store operators to manage the payment themselves but still keep track of its different stages on Medusa.
Adding a Payment Provider is as simple as creating a [service](../../../development/services/create-service.md) file in `src/services`. A Payment Provider is essentially a service that extends `AbstractPaymentService` from the core Medusa package `@medusajs/medusa`.
Payment Provider Services must have a static property `identifier`. It's the name that will be used to install and refer to the Payment Provider in the Medusa backend.
:::tip
Payment Providers are loaded and installed at the backend startup.
:::
The Payment Provider service is also required to implement the following methods:
1. `createPayment`: Called when a Payment Session for the Payment Provider is to be created.
2. `retrievePayment`: Used to retrieve payment data from the third-party provider, if theres any.
3. `getStatus`: Used to get the status of a Payment or Payment Session.
4. `updatePayment`: Used to update the Payment Session whenever the cart and its related data are updated.
5. `updatePaymentData`: Used to update the `data` field of Payment Sessions. Specifically called when a request is sent to the [Update Payment Session](/api/store/#tag/Cart/operation/PostCartsCartPaymentSessionUpdate) endpoint.
6. `deletePayment`: Used to perform any action necessary before a Payment Session is deleted.
7. `authorizePayment`: Used to authorize the payment amount of the cart before the order or swap is created.
8. `getPaymentData`: Used to retrieve the data that should be stored in the `data` field of a new Payment instance after the payment amount has been authorized.
9. `capturePayment`: Used to capture the payment amount of an order or swap.
10. `refundPayment`: Used to refund a payment amount of an order or swap.
11. `cancelPayment`: Used to perform any necessary action with the third-party payment provider when an order or swap is canceled.
:::note
All these methods must be declared async in the Payment Provider Service.
:::
These methods are used at different points in the Checkout flow as well as when processing the order after its placed.
![Checkout Flow - Payment](https://res.cloudinary.com/dza7lstvk/image/upload/v1677770739/Medusa%20Docs/Diagrams/checkout-payment_rouet2.png)
---
## Create a Payment Provider
The first step to create a payment provider is to create a JavaScript or TypeScript file in `src/services`. The file's name should be the name of the payment provider.
For example, create the file `src/services/my-payment.ts` with the following content:
```ts title=src/services/my-payment.ts
import {
AbstractPaymentService, PaymentContext, Data,
Payment, PaymentSession, PaymentSessionStatus,
PaymentSessionData, Cart, PaymentData,
PaymentSessionResponse } from "@medusajs/medusa"
import { EntityManager } from "typeorm"
class MyPaymentService extends AbstractPaymentService {
protected manager_: EntityManager
protected transactionManager_: EntityManager | undefined
async getPaymentData(
paymentSession: PaymentSession
): Promise<Data> {
throw new Error("Method not implemented.")
}
async updatePaymentData(
paymentSessionData: PaymentSessionData,
data: Data
): Promise<PaymentSessionData> {
throw new Error("Method not implemented.")
}
async createPayment(
context: Cart & PaymentContext
): Promise<PaymentSessionResponse> {
throw new Error("Method not implemented.")
}
async retrievePayment(paymentData: Data): Promise<Data> {
throw new Error("Method not implemented.")
}
async updatePayment(
paymentSessionData: PaymentSessionData,
cart: Cart
): Promise<PaymentSessionData> {
throw new Error("Method not implemented.")
}
async authorizePayment(
paymentSession: PaymentSession,
context: Data
): Promise<{
data: PaymentSessionData;
status: PaymentSessionStatus
}> {
throw new Error("Method not implemented.")
}
async capturePayment(payment: Payment): Promise<PaymentData> {
throw new Error("Method not implemented.")
}
async refundPayment(
payment: Payment,
refundAmount: number
): Promise<PaymentData> {
throw new Error("Method not implemented.")
}
async cancelPayment(payment: Payment): Promise<PaymentData> {
throw new Error("Method not implemented.")
}
async deletePayment(
paymentSession: PaymentSession
): Promise<void> {
throw new Error("Method not implemented.")
}
async getStatus(data: Data): Promise<PaymentSessionStatus> {
throw new Error("Method not implemented.")
}
}
export default MyPaymentService
```
Where `MyPaymentService` is the name of your Payment Provider service. For example, Stripes Payment Provider Service is called `StripeProviderService`.
Payment Providers must extend `AbstractPaymentService` from the core Medusa package `@medusajs/medusa`.
:::tip
Following the naming convention of Services, the name of the file should be the slug name of the Payment Provider, and the name of the class should be the camel case name of the Payment Provider suffixed with “Service”. In the example above, the name of the file should be `my-payment.js`. You can learn more in the [service documentation](../../../development/services/create-service.md).
:::
### Identifier
As mentioned in the overview, Payment Providers should have a static `identifier` property.
The `PaymentProvider` entity has 2 properties: `identifier` and `is_installed`. The value of the `identifier` property in the class will be used when the Payment Provider is created in the database.
The value of this property will also be used to reference the Payment Provider throughout the Medusa backend. For example, the identifier is used when a [Payment Session in a cart is selected on checkout](/api/store/#tag/Cart/operation/PostCartsCartPaymentSession).
### constructor
You can use the `constructor` of your Payment Provider to have access to different services in Medusa through dependency injection.
You can also use the constructor to initialize your integration with the third-party provider. For example, if you use a client to connect to the third-party providers APIs, you can initialize it in the constructor and use it in other methods in the service.
Additionally, if youre creating your Payment Provider as an external plugin to be installed on any Medusa backend and you want to access the options added for the plugin, you can access it in the constructor. The options are passed as a second parameter:
```ts
class MyPaymentService extends AbstractPaymentService {
// ...
constructor(container, options) {
super(container)
// you can access options here
}
// ...
}
```
### createPayment
This method is called during checkout when [Payment Sessions are initialized](/api/store/#tag/Cart/operation/PostCartsCartPaymentSessions) to present payment options to the customer. It is used to allow you to make any necessary calls to the third-party provider to initialize the payment.
For example, in Stripe this method is used to initialize a Payment Intent for the customer.
The method receives a context object as a first parameter. This object is of type `PaymentContext` and has the following properties:
```ts
type PaymentContext = {
cart: {
context: Record<string, unknown>
id: string
email: string
shipping_address: Address | null
shipping_methods: ShippingMethod[]
}
currency_code: string
amount: number
resource_id?: string
customer?: Customer
}
```
:::note
Before v1.7.2, the first parameter was of type `Cart`. This method remains backwards compatible, but will be changed in the future. So, it's recommended to change the type of the first parameter to `PaymentContext`.
:::
This method must return an object of type `PaymentSessionResponse`. It should have the following properties:
```ts
type PaymentSessionResponse = {
update_requests: {
customer_metadata: Record<string, unknown>
}
session_data: Record<string, unknown>
}
```
Where:
- `session_data` is the data that is going to be stored in the `data` field of the Payment Session to be created. As mentioned in the [Architecture Overview](../payment.md), 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.
- `update_requests` is an object that can be used to pass data from the payment provider plugin to the core to update internal resources. Currently, it only has one attribute `customer_metadata` which allows updating the `metadata` field of the customer.
An example of a minimal implementation of `createPayment`:
```ts
import {
PaymentContext,
PaymentSessionResponse,
} from "@medusajs/medusa"
class MyPaymentService extends AbstractPaymentService {
// ...
async createPayment(
context: Cart & PaymentContext
): Promise<PaymentSessionResponse> {
// prepare data
return {
session_data,
update_requests,
}
}
}
```
### retrievePayment
This method is used to provide a uniform way of retrieving the payment information from the third-party provider. For example, in Stripes Payment Provider Service this method is used to retrieve the payment intent details from Stripe.
This method accepts the `data` field of a Payment Session or a Payment. So, you should make sure to store in the `data` field any necessary data that would allow you to retrieve the payment data from the third-party provider.
This method must return an object containing the data from the third-party provider.
An example of a minimal implementation of `retrievePayment` where you dont need to interact with the third-party provider:
```ts
import { Data } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService {
// ...
async retrievePayment(paymentData: Data): Promise<Data> {
return {}
}
}
```
### getStatus
This method is used to get the status of a Payment or a Payment Session.
Its main usage is in the place order workflow. If the status returned is not `authorized`, then the payment is considered failed, an error will be thrown, and the order will not be placed.
This method accepts the `data` field of the Payment or Payment Session as a parameter. You can use this data to interact with the third-party provider to check the status of the payment if necessary.
This method returns a string that represents the status. The status must be one of the following values:
1. `authorized`: The payment was successfully authorized.
2. `pending`: The payment is still pending. This is the default status of a Payment Session.
3. `requires_more`: The payment requires more actions from the customer. For example, if the customer must complete a 3DS check before the payment is authorized.
4. `error`: If an error occurred with the payment.
5. `canceled`: If the payment was canceled.
An example of a minimal implementation of `getStatus` where you dont need to interact with the third-party provider:
```ts
import { Data, PaymentSessionStatus } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService {
// ...
async getStatus(data: Data): Promise<PaymentSessionStatus> {
return PaymentSessionStatus.AUTHORIZED
}
}
```
:::note
This code block assumes the status is stored in the `data` field as demonstrated in the `createPayment` method.
:::
### updatePayment
This method is used to perform any necessary updates on the payment. This method is called whenever the cart or any of its related data is updated. For example, when a [line item is added to the cart](/api/store/#tag/Cart/operation/PostCartsCartLineItems) or when a [shipping method is selected](/api/store/#tag/Cart/operation/PostCartsCartShippingMethod).
:::tip
A line item refers to a product in the cart.
:::
It accepts the `data` field of the Payment Session as the first parameter and a context object as a second parameter. This object is of type `PaymentContext` and has the following properties:
```ts
type PaymentContext = {
cart: {
context: Record<string, unknown>
id: string
email: string
shipping_address: Address | null
shipping_methods: ShippingMethod[]
}
currency_code: string
amount: number
resource_id?: string
customer?: Customer
}
```
:::note
Before v1.7.2, the second parameter was of type `Cart`. This method remains backwards compatible, but will be changed in the future. So, it's recommended to change the type of the first parameter to `PaymentContext`.
:::
You can utilize this method to interact with the third-party provider and update any details regarding the payment if necessary.
This method must return an object of type `PaymentSessionResponse`. It should have the following properties:
```ts
type PaymentSessionResponse = {
update_requests: {
customer_metadata: Record<string, unknown>
}
session_data: Record<string, unknown>
}
```
Where:
- `session_data` is the data that is going to be stored in the `data` field of the Payment Session to be created. As mentioned in the [Architecture Overview](../payment.md), 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.
- `update_requests` is an object that can be used to request from the Medusa core to update internal resources. Currently, it only has one attribute `customer_metadata` which allows updating the `metadata` field of the customer.
An example of a minimal implementation of `updatePayment`:
```ts
import {
PaymentSessionData,
Cart,
PaymentContext,
PaymentSessionResponse,
} from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService {
// ...
async updatePayment(
paymentSessionData: PaymentSessionData,
cart: Cart
): Promise<PaymentSessionData> {
// prepare data
return {
session_data,
update_requests,
}
}
}
```
### updatePaymentData
This method is used to update the `data` field of a Payment Session. Particularly, it is called when a request is sent to the [Update Payment Session](/api/store/#tag/Cart/operation/PostCartsCartPaymentSessionUpdate) endpoint. This endpoint receives a `data` object in the body of the request that should be used to update the existing `data` field of the Payment Session.
This method accepts the current `data` field of the Payment Session as the first parameter, and the new `data` field sent in the body request as the second parameter.
You can utilize this method to interact with the third-party provider and make any necessary updates based on the `data` field passed in the body of the request.
This method must return an object that will be stored in the `data` field of the Payment Session.
An example of a minimal implementation of `updatePaymentData` that returns the `updatedData` passed in the body of the request as-is to update the `data` field of the Payment Session.
```ts
import { Data, PaymentSessionData } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService {
// ...
async updatePaymentData(
paymentSessionData: PaymentSessionData,
data: Data
): Promise<PaymentSessionData> {
return updatedData
}
}
```
### deletePayment
This method is used to perform any actions necessary before a Payment Session is deleted. The Payment Session is deleted in one of the following cases:
1. When a request is sent to [delete the Payment Session](/api/store/#tag/Cart/operation/DeleteCartsCartPaymentSessionsSession).
2. When the [Payment Session is refreshed](/api/store/#tag/Cart/operation/PostCartsCartPaymentSessionsSession). The Payment Session is deleted so that a newer one is initialized instead.
3. When the Payment Provider is no longer available. This generally happens when the store operator removes it from the available Payment Provider in the admin.
4. When the region of the store is changed based on the cart information and the Payment Provider is not available in the new region.
It accepts the Payment Session as an object for its first parameter.
You can use this method to interact with the third-party provider to delete data related to the Payment Session if necessary.
An example of a minimal implementation of `deletePayment` where no interaction with a third-party provider is required:
```ts
import { PaymentSession } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService {
// ...
async deletePayment(
paymentSession: PaymentSession
): Promise<void> {
return
}
}
```
### authorizePayment
This method is used to authorize payment using the Payment Session for an order. This is called when the [cart is completed](/api/store/#tag/Cart/operation/PostCartsCartComplete) and before the order is created.
This method is also used for authorizing payments of a swap of an order.
The payment authorization might require additional action from the customer before it is declared authorized. Once that additional action is performed, the `authorizePayment` method will be called again to validate that the payment is now fully authorized. So, you should make sure to implement it for this case as well, if necessary.
Once the payment is authorized successfully and the Payment Session status is set to `authorized`, the order can then be placed.
If the payment authorization fails, then an error will be thrown and the order will not be created.
:::note
The payment authorization status is determined using the `getStatus` method as mentioned earlier. If the status is `requires_more` then it means additional actions are required from the customer. If the workflow process reaches the “Start Create Order” step and the status is not `authorized`, then the payment is considered failed.
:::
This method accepts the Payment Session as an object for its first parameter, and a `context` object as a second parameter. The `context` object contains the following properties:
1. `ip`: The customers IP.
2. `idempotency_key`: The [Idempotency Key](../payment.md#idempotency-key) that is associated with the current cart. It is useful when retrying payments, retrying checkout at a failed point, or for payments that require additional actions from the customer.
This method must return an object containing the property `status` which is a string that indicates the current status of the payment, and the property `data` which is an object containing any additional information required to perform additional payment processing such as capturing the payment. The values of both of these properties are stored in the Payment Sessions `status` and `data` fields respectively.
You can utilize this method to interact with the third-party provider and perform any actions necessary to authorize the payment.
An example of a minimal implementation of `authorizePayment` that doesnt need to interact with any third-party provider:
```ts
import {
Data,
PaymentSession,
PaymentSessionStatus,
PaymentSessionData,
} from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService {
// ...
async authorizePayment(
paymentSession: PaymentSession,
context: Data
): Promise<{
data: PaymentSessionData;
status: PaymentSessionStatus
}> {
return {
status: PaymentSessionStatus.AUTHORIZED,
data: {
id: "test",
},
}
}
}
```
### getPaymentData
After the payment is authorized using `authorizePayment`, a Payment instance will be created. The `data` field of the Payment instance will be set to the value returned from the `getPaymentData` method in the Payment Provider.
This method accepts the Payment Session as an object for its first parameter.
This method must return an object to be stored in the `data` field of the Payment instance. You can either use it as-is or make any changes to it if necessary.
An example of a minimal implementation of `getPaymentData`:
```ts
import { Data, PaymentSession } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService {
// ...
async getPaymentData(
paymentSession: PaymentSession
): Promise<Data> {
return paymentSession.data
}
}
```
### capturePayment
This method is used to capture the payment amount of an order. This is typically triggered manually by the store operator from the admin.
This method is also used for capturing payments of a swap of an order.
You can utilize this method to interact with the third-party provider and perform any actions necessary to capture the payment.
This method accepts the Payment as an object for its first parameter.
This method must return an object that will be stored in the `data` field of the Payment.
An example of a minimal implementation of `capturePayment` that doesnt need to interact with a third-party provider:
```ts
import { Data, Payment } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService {
// ...
async capturePayment(payment: Payment): Promise<Data> {
return {
status: "captured",
}
}
}
```
### refundPayment
This method is used to refund an orders payment. This is typically triggered manually by the store operator from the admin. The refund amount might be the total order amount or part of it.
This method is also used for refunding payments of a swap of an order.
You can utilize this method to interact with the third-party provider and perform any actions necessary to refund the payment.
This method accepts the Payment as an object for its first parameter, and the amount to refund as a second parameter.
This method must return an object that is stored in the `data` field of the Payment.
An example of a minimal implementation of `refundPayment` that doesnt need to interact with a third-party provider:
```ts
import { Data, Payment } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService {
// ...
async refundPayment(
payment: Payment,
refundAmount: number
): Promise<Data> {
return {
id: "test",
}
}
}
```
### cancelPayment
This method is used to cancel an orders payment. This method is typically triggered by one of the following situations:
1. Before an order is placed and after the payment is authorized, an inventory check is done on products to ensure that products are still available for purchase. If the inventory check fails for any of the products, the payment is canceled.
2. If the store operator cancels the order from the admin.
This method is also used for canceling payments of a swap of an order.
You can utilize this method to interact with the third-party provider and perform any actions necessary to cancel the payment.
This method accepts the Payment as an object for its first parameter.
This method must return an object that is stored in the `data` field of the Payment.
An example of a minimal implementation of `cancelPayment` that doesnt need to interact with a third-party provider:
```ts
import { Data, Payment } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService {
// ...
async cancelPayment(payment: Payment): Promise<Data> {
return {
id: "test",
}
}
}
```
---
## Optional Methods
### retrieveSavedMethods
This method can be added to your Payment Provider service if your third-party provider supports saving the customers payment methods. Please note that in Medusa there is no way to save payment methods.
This method is called when a request is sent to [Retrieve Saved Payment Methods](/api/store/#tag/Customer/operation/GetCustomersCustomerPaymentMethods).
This method accepts the customer as an object for its first parameter.
This method returns an array of saved payment methods retrieved from the third-party provider. You have the freedom to shape the items in the array as you see fit since they will be returned as-is for the response to the request.
:::note
If youre using Medusas [Next.js](../../../starters/nextjs-medusa-starter.mdx) or [Gatsby](../../../starters/gatsby-medusa-starter.mdx) storefront starters, note that the presentation of this method is not implemented. Youll need to implement the UI and pages for this method based on your implementation and the provider you are using.
:::
An example of the implementation of `retrieveSavedMethods` taken from Stripes Payment Provider:
```ts
import { Customer, Data } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService {
// ...
/**
* Fetches a customers saved payment methods
* if they're saved in Stripe.
* @param {object} customer - customer to fetch saved cards for
* @return {Promise<Array<object>>} saved payments methods
*/
async retrieveSavedMethods(
customer: Customer
): Promise<Data[]> {
if (customer.metadata && customer.metadata.stripe_id) {
const methods = await this.stripe_.paymentMethods.list({
customer: customer.metadata.stripe_id,
type: "card",
})
return methods.data
}
return Promise.resolve([])
}
}
```
---
## See Also
- Implementation Examples: [Stripe](https://github.com/medusajs/medusa/tree/2e6622ec5d0ae19d1782e583e099000f0a93b051/packages/medusa-payment-stripe) and [PayPal](https://github.com/medusajs/medusa/tree/2e6622ec5d0ae19d1782e583e099000f0a93b051/packages/medusa-payment-paypal) payment providers.