Как добавить профиль клиента в магазин электронной коммерции Next.js

Medusa — это платформа для безголовой коммерции с открытым исходным кодом, цель которой — предоставить разработчикам отличные возможности для работы. Хотя она предоставляет для использования витрины Next.js и Gatsby, вы также можете использовать любую витрину по вашему выбору.

Чтобы сделать работу разработчика еще проще, Medusa предоставляет клиентский пакет NPM, который можно использовать с фреймворками JavaScript и Typescript.

В этом руководстве вы узнаете, как установить и использовать клиент Medusa JS в вашей витрине для реализации потока регистрации и профиля клиента.

Код для этого руководства вы можете найти в этом репозитории GitHub.

Предварительные условия

В этом руководстве предполагается, что у вас уже установлен сервер Medusa. Если это не так, пожалуйста, следуйте краткому руководству по запуску.

Кроме того, в этом руководстве используется стартер Next.js для реализации профиля клиента. Однако в данном руководстве основное внимание будет уделено использованию JS-клиента Medusa. Поэтому вы можете следовать этому руководству, если вы используете другой фреймворк для своей витрины.

Установите Medusa JS Client

В директории проекта витрины установите клиент Medusa JS, если он еще не установлен:

npm install @medusajs/medusa-js
Войдите в полноэкранный режим Выйдите из полноэкранного режима

Если вы используете наши стартеры Next.js или Gatsby, то устанавливать его не нужно.

Инициализация клиента

В стартере Next.js клиент инициализируется в utils/client.js, и вы можете просто импортировать его в свои компоненты. Однако если вы используете свою собственную витрину, вот как вы можете инициализировать клиент Medusa:

const client = new Medusa({ baseUrl: BACKEND_URL });
Войти в полноэкранный режим Выйти из полноэкранного режима

Где BACKEND_URL — это URL вашего сервера Medusa.

Затем вы можете использовать методы и ресурсы клиента для отправки всех типов запросов на ваш сервер Medusa, которые вы увидите позже в этом руководстве.

Добавить стили

Этот шаг необязателен и может быть пропущен. Чтобы добавить некоторые стили для остальной части учебника, вы можете создать файл styles/customer.module.css с этим содержимым.

Добавление клиента в контекст

В стартере Next.js, StoreContext хранит все переменные и методы, важные для магазина, такие как объект cart.

В этом разделе вы добавите переменную и метод в контекст: customer и setCustomer соответственно.

В переменную defaultStoreContext в context/store-context.js добавьте следующее:

export const defaultStoreContext = {
    ...,
    customer: null,
    setCustomer: async () => {}
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Затем в функции reducer добавьте случай для действия setCustomer:

case "setCustomer":
      return {
        ...state,
        customer: action.payload
      }
Войти в полноэкранный режим Выход из полноэкранного режима

Внутри функции StoreProvider добавьте в useEffect под инициализацией корзины следующее действие, чтобы получить клиента, если он вошел в систему, и установить его в контексте:

//try to retrieve customer
client.auth.getSession()
  .then(({customer}) => {
    setCustomer(customer)
  })
  .catch((e) => setCustomer(null))
Войти в полноэкранный режим Выйти из полноэкранного режима

Обратите внимание, что здесь вы используете client.auth.getSession. client был инициализирован ранее в этом файле, поэтому если вы реализуете это в своем собственном магазине, вам нужно инициализировать его перед этим фрагментом кода.

Затем вы используете auth.getSession, который позволяет вам получить текущего зарегистрированного клиента, если таковой имеется.

Если клиент вошел в систему и возвращается в ответе, вы устанавливаете его в контексте, в противном случае вы устанавливаете клиент в null.

Затем добавьте новую функцию внутри StoreProvider для обработки установки клиента:

const setCustomer = (customer) => {
    dispatch({type: 'setCustomer', payload: customer})
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Это просто отправит действие на редуктор, который установит объект customer в контексте.

Наконец, добавьте функцию setCustomer к свойству value в StoreContext.Provider, возвращенному в StoreProvider:

return (
    <StoreContext.Provider
      value={{
        ...,
        setCustomer
      }}
    >
      {children}
    </StoreContext.Provider>
  )
Вход в полноэкранный режим Выход из полноэкранного режима

Создание страниц регистрации и входа

В этом разделе вы создадите страницы регистрации и входа, которые позволят покупателю либо создать учетную запись, либо войти в существующую.

Страница регистрации

Создайте файл pages/sign-up.js со следующим содержимым:

import * as Yup from 'yup';

import { useContext, useEffect, useRef } from 'react';

import StoreContext from '../context/store-context';
import { createClient } from "../utils/client"
import styles from "../styles/customer.module.css";
import { useFormik } from "formik";
import { useRouter } from 'next/router';

export default function SignUp() {
  const router = useRouter();
  const { setCustomer, customer } = useContext(StoreContext)
  useEffect(() => {
    if (customer) {
      router.push("/")
    }
  }, [customer, router])
  const buttonRef = useRef();
  const { handleSubmit, handleBlur, handleChange, values, touched, errors } = useFormik({
    initialValues: {
      email: '',
      first_name: '',
      last_name: '',
      password: ''
    },
    validationSchema: Yup.object().shape({
      email: Yup.string().email().required(),
      first_name: Yup.string().required(),
      last_name: Yup.string().required(),
      password: Yup.string().required()
    }),
    onSubmit: function (values) {
      if (buttonRef.current) {
        buttonRef.current.disabled = true;
      }

      const client = createClient()

      client.customers.create({
        email: values.email,
        first_name: values.first_name,
        last_name: values.last_name,
        password: values.password
      }).then(({ customer }) => {
        setCustomer(customer);
      })
    }
  })
  return (
    <div className={styles.container}>
      <main className={styles.main}>
        <form onSubmit={handleSubmit} className={styles.signForm}>
          <h1>Sign Up</h1>
          <div className={styles.inputContainer}>
            <label htmlFor="email">Email</label>
            <input type="email" name="email" id="email" className={styles.input} 
              onChange={handleChange} onBlur={handleBlur} value={values.email} />
            {errors.email && touched.email && <span className={styles.error}>{errors.email}</span>}
          </div>
          <div className={styles.inputContainer}>
            <label htmlFor="first_name">First Name</label>
            <input type="text" name="first_name" id="first_name" className={styles.input} 
              onChange={handleChange} onBlur={handleBlur} value={values.first_name} />
            {errors.first_name && touched.first_name && <span className={styles.error}>{errors.first_name}</span>}
          </div>
          <div className={styles.inputContainer}>
            <label htmlFor="last_name">Last Name</label>
            <input type="text" name="last_name" id="last_name" className={styles.input} 
              onChange={handleChange} onBlur={handleBlur} value={values.last_name} />
            {errors.last_name && touched.last_name && <span className={styles.error}>{errors.last_name}</span>}
          </div>
          <div className={styles.inputContainer}>
            <label htmlFor="password">Password</label>
            <input type="password" name="password" id="password" className={styles.input} 
              onChange={handleChange} onBlur={handleBlur} value={values.password} />
            {errors.password && touched.password && <span className={styles.error}>{errors.password}</span>}
          </div>
          <div className={styles.inputContainer}>
            <button type="submit" className={styles.btn} ref={buttonRef}>Sign Up</button>
          </div>
        </form>
      </main>
    </div>
  )
}
Вход в полноэкранный режим Выход из полноэкранного режима

На этой странице вы используете Formik и Yup для создания и проверки формы. Эта форма имеет 4 поля: электронная почта, имя, фамилия и пароль. Эти поля необходимы для регистрации клиента в Medusa.

Важным моментом здесь является часть, которая использует клиент Medusa в функции onSubmit, переданной в useFormik:

const client = createClient()

client.customers.create({
  email: values.email,
  first_name: values.first_name,
  last_name: values.last_name,
  password: values.password
}).then(({ customer }) => {
  setCustomer(customer);
})
Вход в полноэкранный режим Выход из полноэкранного режима

Сначала вы инициализируете клиент Medusa. Для этого используется служебная функция в utils/client.js, но если у вас нет такой функции в вашем магазине, вы можете заменить ее инициализацией, упомянутой ранее в руководстве.

Затем вы используете client.customers.create, который отправит запрос на конечную точку создания клиента. Эта конечная точка требует поля email, first_name, last_name, и password.

Если регистрация прошла успешно, будет возвращен объект customer, а в cookies будет сохранен токен сессии, чтобы пользователь мог войти в систему. Вы используете объект customer, чтобы установить клиента в контексте.

Обратите внимание, что в начале компонента вы проверяете, вошел ли уже клиент в систему, и в этом случае перенаправляете его на главную страницу:

useEffect(() => {
  if (customer) {
    router.push("/")
  }
}, [customer, router])
Войти в полноэкранный режим Выход из полноэкранного режима

Страница входа в систему

Далее создайте файл pages/sign-in.js со следующим содержимым:

import * as Yup from 'yup';

import { useContext, useEffect, useRef } from 'react';

import StoreContext from '../context/store-context';
import { createClient } from "../utils/client"
import styles from "../styles/customer.module.css";
import { useFormik } from "formik";
import { useRouter } from 'next/router';

export default function SignIn() {
  const router = useRouter();
  const { setCustomer, customer } = useContext(StoreContext)
  useEffect(() => {
    if (customer) {
      router.push("/")
    }
  }, [customer, router])
  const buttonRef = useRef();
  const { handleSubmit, handleBlur, handleChange, values, touched, errors } = useFormik({
    initialValues: {
      email: '',
      password: ''
    },
    validationSchema: Yup.object().shape({
      email: Yup.string().email().required(),
      password: Yup.string().required()
    }),
    onSubmit: function (values) {
      if (buttonRef.current) {
        buttonRef.current.disabled = true;
      }

      const client = createClient()

      client.auth.authenticate({
        email: values.email,
        password: values.password
      }).then(({ customer }) => {
        setCustomer(customer);
      })
    }
  })
  return (
    <div className={styles.container}>
      <main className={styles.main}>
        <form onSubmit={handleSubmit} className={styles.signForm}>
          <h1>Sign In</h1>
          <div className={styles.inputContainer}>
            <label htmlFor="email">Email</label>
            <input type="email" name="email" id="email" className={styles.input} 
              onChange={handleChange} onBlur={handleBlur} value={values.email} />
            {errors.email && touched.email && <span className={styles.error}>{errors.email}</span>}
          </div>
          <div className={styles.inputContainer}>
            <label htmlFor="password">Password</label>
            <input type="password" name="password" id="password" className={styles.input} 
              onChange={handleChange} onBlur={handleBlur} value={values.password} />
            {errors.password && touched.password && <span className={styles.error}>{errors.password}</span>}
          </div>
          <div className={styles.inputContainer}>
            <button type="submit" className={styles.btn} ref={buttonRef}>Sign In</button>
          </div>
        </form>
      </main>
    </div>
  )
}
Вход в полноэкранный режим Выход из полноэкранного режима

Эта страница также использует Formik и Yup для создания и проверки формы. Этой форме нужны только email и пароль.

Важный бит находится в функции onSubmit, переданной useFormik:

const client = createClient()

client.auth.authenticate({
  email: values.email,
  password: values.password
}).then(({ customer }) => {
  setCustomer(customer);
})
Войти в полноэкранный режим Выйти из полноэкранного режима

Как и раньше, вы начинаете с инициализации клиента Medusa. Затем вы аутентифицируете клиента с помощью функции client.auth.authenticate, которая вызывает конечную точку Authenticate Customer. Эта конечная точка требует 2 параметра: email и password.

В случае успеха возвращается объект клиента, который вы используете для установки клиента в контексте магазина с помощью setCustomer. Также будет установлен файл cookie для поддержания сессии входа для клиента.

Добавление ссылок на панель навигации

Наконец, в components/layout/nav-bar.jsx , инициализируйте клиента из StoreContext:

const { cart, customer } = useContext(StoreContext)
Вход в полноэкранный режим Выйти из полноэкранного режима

Затем в возвращенном JSX добавьте следующее, чтобы добавить ссылку на новые страницы:

{!customer && <Link href="/sign-up">Sign Up</Link>}
{!customer && <Link href="/sign-in">Sign In</Link>}
{customer && <Link href="/customer">Profile</Link>}
Войти в полноэкранный режим Выйти из полноэкранного режима

Обратите внимание, что вы также добавили ссылку на профиль клиента, которую вы реализуете в следующем разделе.

Протестируйте

Сначала убедитесь, что сервер Medusa запущен. Затем запустите сервер для вашей витрины:

npm run dev
Войдите в полноэкранный режим Выйдите из полноэкранного режима

Если вы сейчас откроете localhost:8000, вы увидите, что в навигационной панели появились 2 новые ссылки для регистрации и входа.

Вы можете попробовать нажать на Sign Up и зарегистрироваться как новый пользователь.

Или нажмите Sign In и войдите в систему как существующий пользователь.

После входа в систему вы будете перенаправлены на главную страницу и увидите ссылку Профиль в навигационной панели.

Добавить профиль клиента

Профиль клиента будет состоять из 3 страниц: Редактирование информации о клиенте, просмотр заказов и просмотр адресов.

Создание макета профиля

Начните с создания макета, который будут использовать все страницы профиля.

Создайте components/layout/profile.jsx со следующим содержимым:

import { useContext, useEffect } from 'react';

import Link from 'next/link';
import StoreContext from '../../context/store-context';
import styles from "../../styles/customer.module.css";
import { useRouter } from 'next/router';

export default function Profile ({children, activeLink}) {
  const router = useRouter()
  const { customer } = useContext(StoreContext)
  useEffect(() => {
    if (!customer) {
      router.push('/')
    }
  }, [customer, router])

  return (
    <div className={styles.container}>
      <main className={styles.main}>
        <div className={styles.profile}>
          <div className={styles.profileSidebar}>
            <Link href="/customer">
              <a className={activeLink === 'customer' ? styles.active : ''}>Edit Profile</a>
            </Link>
            <Link href="/customer/orders">
              <a className={activeLink === 'orders' ? styles.active : ''}>Orders</a>
            </Link>
            <Link href="/customer/addresses">
              <a className={activeLink === 'addresses' ? styles.active : ''}>Addresses</a>
            </Link>
          </div>
          <div className={styles.profileContent}>
            {children}
          </div>
        </div>
      </main>
    </div>
  )
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Сначала проверяется клиент и контекст, чтобы узнать, вошел ли пользователь в систему. Затем отображается боковая панель с 3 ссылками, а в основном содержимом отображаются дочерние элементы.

Создание страницы редактирования профиля

Страница редактирования профиля будет основной страницей профиля клиента. Создайте файл pages/customer/index.js со следующим содержимым:

import * as Yup from 'yup';

import { useContext, useRef } from 'react';

import Profile from '../../components/layout/profile';
import StoreContext from "../../context/store-context";
import { createClient } from "../../utils/client"
import styles from "../../styles/customer.module.css";
import { useFormik } from 'formik';

export default function CustomerIndex() {
  const { customer, setCustomer } = useContext(StoreContext)
  const buttonRef = useRef()
  const { handleSubmit, handleChange, handleBlur, values, errors, touched } = useFormik({
    initialValues: {
      email: customer?.email,
      first_name: customer?.first_name,
      last_name: customer?.last_name,
      password: ''
    },
    validationSchema: Yup.object().shape({
      email: Yup.string().email().required(),
      first_name: Yup.string().required(),
      last_name: Yup.string().required(),
      password: Yup.string()
    }),

    onSubmit: (values) => {
      buttonRef.current.disabled = true;

      const client = createClient()

      if (!values.password) {
        delete values['password']
      }

      client.customers.update(values)
        .then(({ customer }) => {
          setCustomer(customer)
          alert("Account updated successfully")
          buttonRef.current.disabled = false;
        })
    }
  })

  return (
    <Profile activeLink='customer'>
      <form onSubmit={handleSubmit}>
        <h1>Edit Profile</h1>
        <div className={styles.inputContainer}>
          <label htmlFor="email">Email</label>
          <input type="email" name="email" id="email" className={styles.input} 
            onChange={handleChange} onBlur={handleBlur} value={values.email} />
          {errors.email && touched.email && <span className={styles.error}>{errors.email}</span>}
        </div>
        <div className={styles.inputContainer}>
          <label htmlFor="first_name">First Name</label>
          <input type="text" name="first_name" id="first_name" className={styles.input} 
            onChange={handleChange} onBlur={handleBlur} value={values.first_name} />
          {errors.first_name && touched.first_name && <span className={styles.error}>{errors.first_name}</span>}
        </div>
        <div className={styles.inputContainer}>
          <label htmlFor="last_name">Last Name</label>
          <input type="text" name="last_name" id="last_name" className={styles.input} 
            onChange={handleChange} onBlur={handleBlur} value={values.last_name} />
          {errors.last_name && touched.last_name && <span className={styles.error}>{errors.last_name}</span>}
        </div>
        <div className={styles.inputContainer}>
          <label htmlFor="password">Password</label>
          <input type="password" name="password" id="password" className={styles.input} 
            onChange={handleChange} onBlur={handleBlur} value={values.password} />
          {errors.password && touched.password && <span className={styles.error}>{errors.password}</span>}
        </div>
        <div className={styles.inputContainer}>
          <button type="submit" className={styles.btn} ref={buttonRef}>Save</button>
        </div>
      </form>
    </Profile>
  )
}
Вход в полноэкранный режим Выход из полноэкранного режима

Это очень похоже на форму регистрации. Вы используете Formik и Yup для создания и проверки формы. Форма имеет 4 входа: email, first_name, last_name, и password, который является необязательным.

Важным здесь является часть в функции onSubmit, передаваемая useFormik:

const client = createClient()

if (!values.password) {
  delete values['password']
}

client.customers.update(values)
  .then(({ customer }) => {
    setCustomer(customer)
    alert("Account updated successfully")
    buttonRef.current.disabled = false;
  })
Войти в полноэкранный режим Выйти из полноэкранного режима

Как и раньше, вы начинаете с инициализации клиента Medusa. Затем, если пароль не установлен, вы удаляете его из списка значений, поскольку вы будете передавать его серверу как есть. Пароль следует передавать только в том случае, если клиент хочет его изменить.

Чтобы обновить информацию о клиенте, вы можете использовать client.customers.update, который отправляет запрос на конечную точку Update Customer. Эта конечная точка принимает несколько дополнительных параметров, включая email, last_name, first_name и password.

Если информация о клиенте обновлена успешно, возвращается объект customer, который вы используете для установки обновленного клиента в контексте. Вы также покажете клиенту оповещение о том, что его учетная запись теперь обновлена.

Если вы сейчас нажмете на ссылку Профиль в навигационной панели, вы увидите страницу профиля с боковой панелью и страницу редактирования профиля в качестве первой страницы.

Попробуйте обновить любую информацию и нажмите кнопку Сохранить. После этого вы увидите сообщение об успешном обновлении.

Страница заказов

Следующая страница, которую вы создадите, — это страница заказов, на которой будут отображаться заказы клиентов.

Создайте новый файл pages/customer/orders.js со следующим содержимым:

import { useEffect, useState } from 'react';

import Link from 'next/link';
import Profile from '../../components/layout/profile';
import { createClient } from "../../utils/client"
import { formatMoneyAmount } from '../../utils/prices';
import styles from "../../styles/customer.module.css";
import { useRouter } from 'next/router';

export default function Orders () {
  const [orders, setOrders] = useState([])
  const [pages, setPages] = useState(0)
  const router = useRouter()
  const p = router.query.p ? parseInt(router.query.p - 1) : 0

  useEffect(() => {
    const client = createClient()

    client.customers.listOrders({
      limit: 20,
      offset: 20 * p
    }).then((result) => {
      setOrders(result.orders)
      setPages(Math.ceil(result.count / result.limit))
    })
  }, [p])

  return (
    <Profile activeLink='orders'>
      <h1>Orders</h1>
      <table className={styles.table}>
        <thead>
          <tr>
            <th>ID</th>
            <th>Total</th>
            <th>Status</th>
          </tr>
        </thead>
        <tbody>
          {orders.map((order) => (
            <tr key={order.id}>
              <td>{order.id}</td>
              <td>{formatMoneyAmount({
                currencyCode: order.currency_code,
                amount: order.total
              }, 2)}</td>
              <td>{order.status}</td>
            </tr>
          ))}
        </tbody>
      </table>
      <div className={styles.pagination}>
        {pages > 0 && p > 1 && (
          <Link href={`/customer/orders?p=${p - 1}`}>Previous</Link>
        )}
        {pages > 1 && p > 0 && p < pages && <span> - </span>}
        {pages > 1 && (p + 1) < pages && (
          <Link href={`/customer/orders?p=${p + 1}`}>Next</Link>
        )}
      </div>
    </Profile>
  )
}
Вход в полноэкранный режим Выход из полноэкранного режима

Сначала вы создаете переменную состояния orders, которая начинается как пустая. У вас также есть переменная состояния pages для отслеживания количества доступных страниц. Затем, в useEffect, вы получаете заказы с помощью клиента Medusa:

useEffect(() => {
  const client = createClient()

  client.customers.listOrders({
    limit: 20,
    offset: 20 * p
  }).then((result) => {
    setOrders(result.orders)
    setPages(Math.ceil(result.count / result.limit))
  })
}, [p])
Войти в полноэкранный режим Выход из полноэкранного режима

После инициализации клиента вы получаете заказы клиента с помощью метода client.customers.listOrders. Этот метод отправляет запрос на конечную точку Retrieve Customer Orders. Эта конечная точка принимает 4 поля: limit, offset, fields и expand. Здесь вы просто используете limit и offset.

limit указывает, сколько заказов должно быть получено на странице, а offset указывает, сколько заказов нужно пропустить с начала, чтобы получить заказы текущей страницы.

Этот запрос возвращает список заказов, а также дополнительные поля, важные для пагинации, включая count, который указывает общее количество заказов, и limit, который является текущим установленным лимитом.

Вы устанавливаете переменную состояния orders на заказы, возвращенные из метода, и устанавливаете количество страниц на основе полей count и limit.

Наконец, вы отображаете заказы в таблице, показывая ID, общее количество и статус. Вы также показываете ссылки пагинации для «Предыдущего» и «Следующего», если это применимо для данной страницы. Это сделано для простоты учебного пособия.

Если вы сейчас откроете страницу заказов, то увидите список заказов вашего клиента, если таковые у него имеются.

Страница адресов

Последняя страница, которую вы создадите в профиле, — это страница адресов, которая позволит клиенту видеть свои адреса доставки.

Создайте файл pages/customer/addresses.js со следующим содержимым:

import Profile from "../../components/layout/profile"
import StoreContext from "../../context/store-context"
import styles from "../../styles/customer.module.css"
import { useContext } from 'react'

export default function Addresses() {
  const { customer } = useContext(StoreContext)

  return (
    <Profile activeLink='addresses'>
      <h1>Addresses</h1>
      {customer && customer.shipping_addresses.length === 0 && <p>You do not have any addresses</p>}
      {customer && customer.shipping_addresses.map((address) => (
        <div key={address.id} className={styles.address}>
          <span><b>First Name:</b> {address.first_name}</span>
          <span><b>Last Name:</b> {address.last_name}</span>
          <span><b>Company:</b> {address.company}</span>
          <span><b>Address Line 1:</b> {address.address_1}</span>
          <span><b>Address Line 2:</b> {address.address_2}</span>
          <span><b>City:</b> {address.city}</span>
          <span><b>Country:</b> {address.country}</span>
        </div>
      ))}
    </Profile>
  )
}
Вход в полноэкранный режим Выход из полноэкранного режима

Вы используете поле shipping_addresses в объекте customer, хранящемся в контексте, и отображаете адреса один за другим. Вы также можете получить доступ к адресу выставления счета, если он есть у клиента, используя customer.billing_address.

Если вы сейчас перейдете на страницу «Адреса», то увидите список адресов доставки клиента.

Что дальше

Используя JS-клиент Medusa, вы можете легко взаимодействовать с API-интерфейсами для создания еще более удобного обслуживания клиентов в вашем магазине.

В этом руководстве для простоты не были рассмотрены все аспекты профиля. Вот что еще вы можете реализовать:

  1. Просмотр отдельных заказов: Вы можете использовать объект заказов, который вы уже извлекли на странице заказов, чтобы показать информацию об одном заказе, или вы можете использовать конечную точку Retrieve Order, которая доступна в клиенте под client.orders.retrieve.
  2. Управление адресами: Вы можете использовать конечную точку Update Shipping Address, доступную в клиенте под client.customers.addresses.updateAddress; вы можете использовать конечную точку Add Shipping Address, доступную в клиенте под client.customers.addresses.addAddress; и вы можете использовать конечную точку Delete Shipping Address, доступную в клиенте под client.customers.addresses.deleteAddress.
  3. Сохраненные методы оплаты: Вы можете получить сохраненные методы оплаты, если вы используете провайдера, который поддерживает сохранение методов оплаты, например, Stripe, используя конечную точку Retrieve Saved Payment Methods, доступную в клиенте в разделе client.customers.paymentMethods.list.
  4. Сброс пароля: Вы можете добавить функцию сброса пароля с помощью конечной точки Create Reset Password Token, доступной в клиенте в разделе client.customers.generatePasswordToken, а затем вы можете сбросить пароль с помощью конечной точки Reset Customer Password, доступной в клиенте в разделе client.customers.resetPassword.

Вы можете ознакомиться с полным списком конечных точек в документации API, чтобы узнать, какие еще функции вы можете добавить в свою витрину.

Если у вас возникнут какие-либо проблемы или вопросы, связанные с Medusa, обращайтесь к команде Medusa через Discord.

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *