docs: changed details related to new payment method signatures (#2876)

* docs: changed details related to new method signatures

* updated the information regarding update_requests

* small changes to the text

* lint fixes

* fixed constructor

* fixes based on review
This commit is contained in:
Shahed Nasser
2023-01-05 11:55:27 +02:00
committed by GitHub
parent af7e67e827
commit 8ba27a2e28
2 changed files with 155 additions and 86 deletions

View File

@@ -50,54 +50,64 @@ The first step to create a payment provider is to create a JavaScript or TypeScr
For example, create the file `src/services/my-payment.ts` with the following content:
<!-- eslint-disable max-len -->
```ts title=src/services/my-payment.ts
import {
AbstractPaymentService,
Cart, Data, Payment, PaymentSession,
PaymentSessionStatus, TransactionBaseService,
} from "@medusajs/medusa"
AbstractPaymentService, PaymentContext, Data,
Payment, PaymentSession, PaymentSessionStatus,
PaymentSessionData, Cart, PaymentData,
PaymentSessionResponse } from "@medusajs/medusa"
import { EntityManager } from "typeorm"
class MyPaymentService extends AbstractPaymentService<TransactionBaseService> {
class MyPaymentService extends AbstractPaymentService {
protected manager_: EntityManager
protected transactionManager_: EntityManager
protected transactionManager_: EntityManager | undefined
getPaymentData(paymentSession: PaymentSession): Promise<Data> {
async getPaymentData(paymentSession: PaymentSession): Promise<Data> {
throw new Error("Method not implemented.")
}
updatePaymentData(paymentSessionData: Data, data: Data): Promise<Data> {
async updatePaymentData(
paymentSessionData: PaymentSessionData,
data: Data
): Promise<PaymentSessionData> {
throw new Error("Method not implemented.")
}
createPayment(cart: Cart): Promise<Data> {
async createPayment(
context: Cart & PaymentContext
): Promise<PaymentSessionResponse> {
throw new Error("Method not implemented.")
}
retrievePayment(paymentData: Data): Promise<Data> {
async retrievePayment(paymentData: Data): Promise<Data> {
throw new Error("Method not implemented.")
}
updatePayment(paymentSessionData: Data, cart: Cart): Promise<Data> {
async updatePayment(
paymentSessionData: PaymentSessionData,
cart: Cart
): Promise<PaymentSessionData> {
throw new Error("Method not implemented.")
}
authorizePayment(
async authorizePayment(
paymentSession: PaymentSession,
context: Data
): Promise<{ data: Data; status: PaymentSessionStatus; }> {
): Promise<{ data: PaymentSessionData; status: PaymentSessionStatus }> {
throw new Error("Method not implemented.")
}
capturePayment(payment: Payment): Promise<Data> {
async capturePayment(payment: Payment): Promise<PaymentData> {
throw new Error("Method not implemented.")
}
refundPayment(payment: Payment, refundAmount: number): Promise<Data> {
async refundPayment(
payment: Payment,
refundAmount: number
): Promise<PaymentData> {
throw new Error("Method not implemented.")
}
cancelPayment(payment: Payment): Promise<Data> {
async cancelPayment(payment: Payment): Promise<PaymentData> {
throw new Error("Method not implemented.")
}
deletePayment(paymentSession: PaymentSession): Promise<void> {
async deletePayment(paymentSession: PaymentSession): Promise<void> {
throw new Error("Method not implemented.")
}
getStatus(data: Data): Promise<PaymentSessionStatus> {
async getStatus(data: Data): Promise<PaymentSessionStatus> {
throw new Error("Method not implemented.")
}
@@ -132,13 +142,11 @@ You can also use the constructor to initialize your integration with the third-p
Additionally, if youre creating your Payment Provider as an external plugin to be installed on any Medusa server 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:
<!-- eslint-disable max-len -->
```ts
class MyPaymentService extends AbstractPaymentService<TransactionBaseService> {
class MyPaymentService extends AbstractPaymentService {
// ...
constructor({ productService }, options) {
super()
constructor(container, options) {
super(container)
// you can access options here
}
// ...
@@ -147,26 +155,62 @@ class MyPaymentService extends AbstractPaymentService<TransactionBaseService> {
### createPayment
This method is called during checkout when [Payment Sessions are initialized](https://docs.medusajs.com/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.
This method is called during checkout when [Payment Sessions are initialized](https://docs.medusajs.com/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.
The method receives the cart as an object for its first parameter. It holds all the necessary information you need to know about the cart and the customer that owns this cart.
For example, in Stripe this method is used to initialize a Payment Intent for the customer.
This method must return an object that is going to be stored in the `data` field of the Payment Session to be created. As mentioned in the [Architecture Overview](./overview.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.
An example of a minimal implementation of `createPayment` that does not interact with any third-party providers:
<!-- eslint-disable max-len -->
The method receives a context object as a first parameter. This object is of type `PaymentContext` and has the following properties:
```ts
import { Cart, Data } from "@medusajs/medusa"
// ...
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
}
```
class MyPaymentService extends AbstractPaymentService<TransactionBaseService> {
:::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](./overview.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(cart: Cart): Promise<Data> {
async createPayment(
context: Cart & PaymentContext
): Promise<PaymentSessionResponse> {
// prepare data
return {
id: "test-payment",
status: "pending",
session_data,
update_requests,
}
}
}
@@ -182,13 +226,11 @@ This method must return an object containing the data from the third-party provi
An example of a minimal implementation of `retrievePayment` where you dont need to interact with the third-party provider:
<!-- eslint-disable max-len -->
```ts
import { Data } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService<TransactionBaseService> {
class MyPaymentService extends AbstractPaymentService {
// ...
async retrievePayment(paymentData: Data): Promise<Data> {
return {}
@@ -214,13 +256,11 @@ This method returns a string that represents the status. The status must be one
An example of a minimal implementation of `getStatus` where you dont need to interact with the third-party provider:
<!-- eslint-disable max-len -->
```ts
import { Data, PaymentSessionStatus } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService<TransactionBaseService> {
class MyPaymentService extends AbstractPaymentService {
// ...
async getStatus(data: Data): Promise<PaymentSessionStatus> {
return PaymentSessionStatus.AUTHORIZED
@@ -244,24 +284,68 @@ A line item refers to a product in the cart.
:::
It accepts the `data` field of the Payment Session as the first parameter and the cart as an object for the second parameter.
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 that will be stored in the `data` field of the Payment Session.
An example of a minimal implementation of `updatePayment` that does not need to make any updates on the third-party provider or the `data` field of the Payment Session:
<!-- eslint-disable max-len -->
This method must return an object of type `PaymentSessionResponse`. It should have the following properties:
```ts
import { Cart, Data } from "@medusajs/medusa"
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](./overview.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<TransactionBaseService> {
class MyPaymentService extends AbstractPaymentService {
// ...
async updatePayment(paymentSessionData: Data, cart: Cart): Promise<Data> {
return paymentSessionData
async updatePayment(
paymentSessionData: PaymentSessionData,
cart: Cart
): Promise<PaymentSessionData> {
// prepare data
return {
session_data,
update_requests,
}
}
}
```
@@ -278,18 +362,16 @@ This method must return an object that will be stored in the `data` field of the
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.
<!-- eslint-disable max-len -->
```ts
import { Data } from "@medusajs/medusa"
import { Data, PaymentSessionData } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService<TransactionBaseService> {
class MyPaymentService extends AbstractPaymentService {
// ...
async updatePaymentData(
paymentSessionData: Data,
updatedData: Data
): Promise<Data> {
paymentSessionData: PaymentSessionData,
data: Data
): Promise<PaymentSessionData> {
return updatedData
}
}
@@ -310,13 +392,11 @@ You can use this method to interact with the third-party provider to delete data
An example of a minimal implementation of `deletePayment` where no interaction with a third-party provider is required:
<!-- eslint-disable max-len -->
```ts
import { PaymentSession } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService<TransactionBaseService> {
class MyPaymentService extends AbstractPaymentService {
// ...
async deletePayment(paymentSession: PaymentSession): Promise<void> {
return
@@ -353,22 +433,21 @@ You can utilize this method to interact with the third-party provider and perfor
An example of a minimal implementation of `authorizePayment` that doesnt need to interact with any third-party provider:
<!-- eslint-disable max-len -->
```ts
import {
Data,
PaymentSession,
PaymentSessionStatus,
PaymentSessionData,
} from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService<TransactionBaseService> {
class MyPaymentService extends AbstractPaymentService {
// ...
async authorizePayment(
paymentSession: PaymentSession,
context: Data
): Promise<{ data: Data; status: PaymentSessionStatus; }> {
): Promise<{ data: PaymentSessionData; status: PaymentSessionStatus }> {
return {
status: PaymentSessionStatus.AUTHORIZED,
data: {
@@ -389,13 +468,11 @@ This method must return an object to be stored in the `data` field of the Paymen
An example of a minimal implementation of `getPaymentData`:
<!-- eslint-disable max-len -->
```ts
import { Data, PaymentSession } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService<TransactionBaseService> {
class MyPaymentService extends AbstractPaymentService {
// ...
async getPaymentData(paymentSession: PaymentSession): Promise<Data> {
return paymentSession.data
@@ -417,13 +494,11 @@ This method must return an object that will be stored in the `data` field of the
An example of a minimal implementation of `capturePayment` that doesnt need to interact with a third-party provider:
<!-- eslint-disable max-len -->
```ts
import { Data, Payment } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService<TransactionBaseService> {
class MyPaymentService extends AbstractPaymentService {
// ...
async capturePayment(payment: Payment): Promise<Data> {
return {
@@ -447,13 +522,11 @@ This method must return an object that is stored in the `data` field of the Paym
An example of a minimal implementation of `refundPayment` that doesnt need to interact with a third-party provider:
<!-- eslint-disable max-len -->
```ts
import { Data, Payment } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService<TransactionBaseService> {
class MyPaymentService extends AbstractPaymentService {
// ...
async refundPayment(
payment: Payment,
@@ -483,13 +556,11 @@ This method must return an object that is stored in the `data` field of the Paym
An example of a minimal implementation of `cancelPayment` that doesnt need to interact with a third-party provider:
<!-- eslint-disable max-len -->
```ts
import { Data, Payment } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService<TransactionBaseService> {
class MyPaymentService extends AbstractPaymentService {
// ...
async cancelPayment(payment: Payment): Promise<Data> {
return {
@@ -521,13 +592,11 @@ If youre using Medusas [Next.js](../../../starters/nextjs-medusa-starter.m
An example of the implementation of `retrieveSavedMethods` taken from Stripes Payment Provider:
<!-- eslint-disable max-len -->
```ts
import { Customer, Data } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService<TransactionBaseService> {
class MyPaymentService extends AbstractPaymentService {
// ...
/**
* Fetches a customers saved payment methods if registered in Stripe.

View File

@@ -76,20 +76,20 @@ Although this change is currently backwards compatible, it is recommended to cha
<!-- eslint-disable max-len -->
```ts
import { PaymentContext, PaymentSessionResponse } from "@medusajs/medusa"
import { Cart, PaymentSessionData, PaymentContext, PaymentSessionResponse } from "@medusajs/medusa"
// ...
class MyPaymentService extends AbstractPaymentService<TransactionBaseService> {
// ...
async createPayment(
context: PaymentContext
context: Cart & PaymentContext
): Promise<PaymentSessionResponse> {
// ...
}
async updatePayment(
paymentSessionData: PaymentSessionData,
context: PaymentContext
context: Cart & PaymentContext
): Promise<PaymentSessionResponse> {
// ...
}