Вы создали отличное дополнение для экосистемы Google Workspace (Документы Google, Таблицы Google, Gmail и др.). Это набирает обороты, и вы чувствуете, что можете монетизировать это. Если это ваша текущая ситуация, отлично! Мы были в такой же ситуации несколько лет назад в Mailmeteor, и мы полностью понимаем, насколько вы взволнованы сегодня.

Ниже приведено руководство, которое я хотел бы найти несколько лет назад, когда искал способы монетизации нашего надстройка для слияния почты для Google Sheets.

Существует несколько способов управления монетизацией надстроек. Я поделюсь с вами тем, как мы это делаем в Mailmeteor. Я считаю, что наш подход обеспечивает наилучшее взаимодействие с пользователем (для ваших пользователей) и высокую безопасность (для вас, разработчика).

Кроме того, в Mailmeteor мы сильно полагаемся на Firebase, которая имеет базу данных NoSQL (база данных в реальном времени) и может выполнять бессерверные функции (облачные функции). Мы используем оба для обработки лицензирования. Если вы еще не знакомы с Firebase, читайте дальше. Общая концепция по-прежнему применима, даже если вы предпочитаете AWS или других облачных провайдеров.


Шаг 1. Аутентификация

Во-первых, вам нужно аутентифицировать ваших пользователей. Когда вы создаете надстройку, иногда у ваших пользователей нет возможности пройти аутентификацию. Это особенно верно для узких надстроек (предоставляющих ограниченный набор функций). Дело в том, что как только вы захотите монетизировать свое дополнение, вам необходимо предоставить процесс регистрации для безопасной аутентификации пользователей и получения их статуса (например, бесплатный или платный).

Сценарий Google Apps уже предоставляет механизм проверки подлинности, поэтому нет необходимости начинать с нуля. С использованием UserSession.getEmail() вы можете с уверенностью знать, какой пользователь запускает ваш код. Это очень хорошее начало. Кроме того, если вы выберете этот путь (что мы и сделали в Mailmeteor), вам не нужно запрашивать у пользователей форму электронной почты/пароля. Пользователь уже вошел в систему. Это хорошая пятерка для ваших пользователей, плюс это сэкономит вам часы работы.

При этом теперь вам нужно заставить скрипт Google Apps общаться с вашим бэкэндом. Как я уже сказал, в Mailmetoer мы используем облачные функции для нашего бэкэнда. Преимущество этого заключается в том, что он относительно дешев в начале и автоматически масштабируется по мере роста надстройки. Но облачные функции не являются обязательным требованием, вы можете получить дешевую каплю за 4 доллара на DigitalOcean, и это будет работать так же хорошо.

Чтобы скрипт Google Apps работал с вашей серверной частью, вы можете создать User class в вашем коде скрипта приложений. И добавьте getProfile метод в нем. Этот метод будет получать профиль пользователя каждый раз, когда вы вызываете функцию. Вот суть нашего user.gs файл в скрипте приложений:

export class User {
  public getProfile() {
    const userId = this.getUserIdFromEmail(Session.getEffectiveUser().getEmail())
    const request = {
      url: BACKEND_ENDPOINT_ + '/user?uid=' + (email),
      options: {
        method: 'GET',
        headers: {
          Authorization: 'Bearer ' + BACKEND_TOKEN_
        }
      }
    }

    const response = UrlFetchApp.fetch(request)
    const profile = JSON.parse(response.getContentText())

    return profile
  }

  public getUserIdFromEmail(email) {
    const hash = `SOME_RANDOM_STRING-${email.toLowerCase().trim()}`
    const digest = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, hash);

    return digest
      .map(function(byte) {
        const v = byte < 0 ? 256 + byte : byte;
        return ("0" + v.toString(16)).slice(-2);
      })
      .join("");
  }
}
Войти в полноэкранный режим

Выйти из полноэкранного режима

Чтобы получить аутентифицированный профиль пользователя, вы должны запустить new User().getProfile(). Как видите, мы полагаемся на Session.getEffectiveUser().getEmail() для безопасного получения электронной почты аутентифицированного пользователя. Этот метод доступен через скрипт Google Apps через Session учебный класс.

Кроме того, вы могли заметить, что идентификатор пользователя генерируется getUserIdFromEmail который просто SHA-256 электронной почты пользователя. Это не выдержит грубой силы, поэтому мы добавляем к нему случайную строку, чтобы было труднее угадать.

Иногда ваш аддон может использовать скрипт приложений для создавать и обслуживать HTML, например, показывая модальное окно или боковую панель. В таком случае вам нужно будет создать функцию в вашем user.gs файл: function getUserProfile() { return new User().getProfile() }. Затем вы можете получить профиль пользователя со страницы HTML, запустив google.script.run.getUserProfile().

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


Шаг 2. Создание пользователя

В вашем бэкенде вам нужно будет создать /user конечная точка, которая будет получать профиль пользователя. Опять же, Firebase предоставляет отличную среду для этого:

export const profile = async (request, response, next) => {
  try {
    // Retrieve params
    const userId = request.query.uid;

    // Validation
    if (typeof userId !== 'string' || !userId) throw new Error('invalid_params');

    // Retrieve the profile from Firebase Real-time Database
    const userRef = FirebaseDatabase.ref(`users/${uid}`);
    const userSnap = await userRef.once('value');
    const profile = userSnap.val() || {};

    // Send response to client
    response.status(200);
    response.send(profile);
    response.end();
  } catch (error) {
    next(error);
  }
};
Войти в полноэкранный режим

Выйти из полноэкранного режима

Идентификатор пользователя, сгенерированный в Apps Script. Мы используем его в качестве ключа в нашей базе данных реального времени Firebase для получения профиля пользователя. Это очень легко реализовать. Перед выпуском в рабочую среду обязательно защитите свое приложение, защитив базу данных от чтения/записи (узнать, как это сделать.

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

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


Шаг 3. Интеграция с Stripe

Итак, давайте подведем итоги: из вашего дополнения, используя Apps Script и Firebase, теперь вы можете безопасно получить профиль пользователя. Все, что вам нужно сделать сейчас, это обновить профиль пользователя, когда кто-то платит вам.

Мы используем Stripe, потому что это относительно интуитивно понятная платежная платформа, но следующее также будет работать с учетной записью PayPal или любой другой службой. Мы используем Stripe Checkout в Mailmeteor для обработки процесса оформления заказа. Если вы хотите самостоятельно управлять процессом оформления заказа, вы можете сделать это согласно их документам.

Здесь мы сосредоточимся на Полосатые вебхуки. В Stripe вы можете создавать веб-хуки, чтобы Stripe мог информировать вас, когда что-то происходит. Есть вебхуки практически для чего угодно (новый клиент, новая подписка, новый платеж, новый счет и т. д.).

Вебхук, который вы будете использовать, зависит от бизнес-модели:

  • для пожизненной подписки (т.е. пользователь платит вам один раз и получает полный доступ к программному обеспечению на всю оставшуюся жизнь), вам необходимо слушать checkout.session.completed мероприятие.
  • для повторяющихся подписок (т. е. вы создаете SaaS) вам необходимо прослушивать несколько веб-перехватчиков. Как минимум: webhook.checkout.session.completed а также customer.subscription.deleted. И в зависимости от того, насколько продвинута ваша SaaS, вам также может понадобиться customer.subscription.updated а также invoice.* События.

Для простоты я покажу вам, как слушать checkout.session.completed. И если бизнес-модель вашей надстройки требует большего, вам решать, как обрабатывать больше событий веб-перехватчиков.

export const webhook = async (req, res, next) => {
  // Stripe webhook request signature
  const sig = req.get('stripe-signature')

  // Stripe webhook raw request
  // Note: "rawBody" is a Firebase method
  // @see: 
  const rawBody = req.rawBody

  try {
    // Verify Stripe request
    const event = stripe.webhooks.constructEvent(rawBody, sig, STRIPE_WEBHOOK_SECRET)

    // Handle the event
    switch (event.type) {
      case 'checkout.session.completed':
        const session = event.data.object
        const customer = (await stripe.customers.retrieve(session.customer))

        // Update profile in Firebase RTDB
        const uid = getUserIdFromEmail(customer.email)
        const userRef = await FirebaseDatabase.ref(`users/${uid}`);
        await userRef.update({ paid: true, customer: customer.id })

        break
    }
  }
}
Войти в полноэкранный режим

Выйти из полноэкранного режима

webhook функция обновляет профиль пользователя со статусом paid = true и идентификатор клиента (подробнее об этом позже). Это означает, что начиная с этого момента, всякий раз, когда вы получаете профиль пользователя, вы можете показывать ему все свои платные функции.

После того, как вы развернули webhook в Firebase, вы можете настроить веб-хук в Stripe Dashboard. Просто получите URL-адрес вашей функции в консоли Firebase и вставьте его в Stripe.

Страница настроек веб-хуков Stripe Dashboard

Я настоятельно рекомендую вам использовать тестовый режим Stripe, чтобы убедиться, что все работает отлично.


Бонус: клиентский портал

Когда вы начнете монетизировать свое дополнение, ваши пользователи будут регулярно запрашивать у вас свои счета. Если вы не хотите тратить 50% своей поддержки на эти запросы, лучше всего создать клиентский портал, где пользователи смогут самостоятельно получать свои счета.

Хорошие новости: Stripe может вам в этом помочь! Из вашего дополнения в пользовательском интерфейсе вы можете добавить новое представление, в котором пользователи могут управлять своими учетными записями.

Загрузите свои счета в надстройке Mailmeteor

Чтобы добавить ссылку на клиентский портал, вам необходимо перенаправить пользователей в Cloud Functions, что создаст полосу URL-адреса на клиентском портале. portal функция похожа на эту:

export const portal = async (re, res, next) => {
  const userId = req.body && req.body.user;
  const customerId = req.body && req.body.customer;

  if (typeof userId !== 'string' || !userId) throw new Error('invalid_params');
  if (typeof customerId !== 'string' || !customerId) throw new Error('invalid_params');

  const customer = await stripe.customers.retrieve(customerId);
  const userEmail = getUserIdFromEmail(customer.email);

  if (userId !== userEmail) throw new Error('invalid_customer');

  const session = await stripe.billingPortal.sessions.create({
    customer: customer.id,
  });

  res.redirect(session.url);
};
Войти в полноэкранный режим

Выйти из полноэкранного режима

customer значение сохраняется на этапе веб-перехватчика, поэтому вы можете легко использовать его повторно. Кроме того, для большей безопасности мы дважды проверяем, соответствует ли электронная почта клиента в Stripe предоставленному идентификатору пользователя.

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

Если вам нужна дополнительная помощь, чтобы соединить точки, пожалуйста, не стесняйтесь обратиться ко мне. А если у вас еще нет дополнения, найдите вдохновение в нашем лучший каталог дополнений Google.