跳转到主要内容

概述

在本教程中,您将设置一个常见的预订工作流程:
  • 在用户完成预订后发送预订确认邮件。
  • 如果用户开始预订但未及时完成,则发送恢复邮件。
完成后,您将拥有:
  • 两个自定义事件(booking_startedbooking_complete
  • 一个根据完成与放弃情况分支的 Journey
  • 用于确认详情的预订数据源
  • 用于恢复激励的可选优惠券数据源
本指南重点介绍 OneSignal 的配置。您的预订系统和后端可以用任何语言或框架实现。

设置流程

  1. 您的应用跟踪 booking_started 自定义事件
  2. 这会将用户加入 Journey。
  3. Journey 等待 booking_complete 事件,如果未及时收到,则发送后续提醒。
  4. 如果预订完成,OneSignal 在发送时调用预订数据源,并发送包含最新预订详情的确认邮件。
  5. 如果预订未在等待窗口内完成,Journey 会走过期路径并发送恢复邮件。

设置

先决条件

开始之前,请确保您具备:
  • 启用了 邮件 渠道的 OneSignal 应用
  • 可以以 JSON 格式返回预订和/或优惠券数据的后端端点
  • 在您的应用、后端和 OneSignal 外部 ID 之间共享的稳定用户标识符
  • 访问 自定义事件

1. 跟踪预订事件

跟踪以下自定义事件。这些可以来自您的应用(使用我们的 SDK)或后端(使用我们的 REST API)。 事件名称:
  • booking_started — 当用户开始预订流程时
  • booking_complete — 当预订成功完成时
在我们的 移动 SDK 和/或 网页 SDK 上使用 trackEvent() 方法直接从您的应用/网站发送自定义事件。
示例
OneSignal.User.trackEvent("booking_started");
OneSignal.User.trackEvent("booking_complete");
跟踪事件和从后端返回数据时使用相同的用户身份。不匹配的 ID 是缺少个性化的最常见原因。

2. 创建数据源别名

在 OneSignal 中,转到 设置 > 数据源 并创建以下别名。 预订数据源: 使用此源在发送时提取最新预订详情。
  • 别名: booking_data
  • 方法: GET
  • URL:
示例端点
https://your-domain.com/datafeed/booking?user_id={{subscription.external_id}}
示例响应:
JSON
{
  "first_name": "Sam",
  "last_booking": {
    "service_type": "Consultation",
    "booking_date": "January 22, 2026",
    "booking_time": "2:00 PM",
    "price": 45
  }
}
优惠券数据源(可选): 如果您想在恢复邮件中包含优惠券代码,请使用此可选源。
  • 别名: coupon
  • 方法: GET
  • URL:
示例端点
https://your-domain.com/datafeed/coupon?user_id={{subscription.external_id}}
示例响应:
JSON
{
  "first_name": "Sam",
  "code": "PROMO8F3K2",
  "discount_text": "10%",
  "expires_in_hours": 2,
  "deep_link": "https://your-domain.com/checkout?coupon=PROMO8F3K2"
}
保护您的数据源端点。在生产环境中,在请求标头中发送 API 密钥(例如 x-api-key),并在 设置 > 数据源 中配置该标头,而不是在 URL 中嵌入密钥。

3. 创建邮件模板

预订确认邮件:

主题:
您的预订详情
正文:
您好 {{ data_feed.booking_data.first_name | default: "客户" }}

感谢您的预订!以下是您的预约详情:

服务:{{ data_feed.booking_data.last_booking.service_type }}
日期:{{ data_feed.booking_data.last_booking.booking_date }}
时间:{{ data_feed.booking_data.last_booking.booking_time }}
价格:${{ data_feed.booking_data.last_booking.price }}

我们期待与您见面!

预订恢复邮件

主题:
完成您的预订并享受优惠
正文:
您好 {{ data_feed.coupon.first_name | default: "客户" }}

在接下来的 {{ data_feed.coupon.expires_in_hours }} 小时内完成您的预订,
使用此代码即可享受 {{ data_feed.coupon.discount_text }} 优惠:

{{ data_feed.coupon.code }}

请在这里使用:
{{ data_feed.coupon.deep_link }}
在 Liquid 中始终包含 default 过滤器,以防止数据源字段缺失时出现空白内容。

4. 构建 Journey

  1. 在 OneSignal 中,转到 消息 > Journeys > 创建 Journey
  2. 入口触发器 设置为:
    • 自定义事件: booking_started
  3. 添加 等待直到 步骤:
    • 条件: 发生自定义事件
    • 事件名称: booking_complete
    • 最大等待时间: 10 分钟
    • 启用过期路径
  4. 配置分支:
    • 已完成: 发送预订确认邮件
      • 数据源: booking_data
    • 已过期: 发送恢复邮件
      • 数据源: coupon
过期分支允许您处理放弃情况,无需在应用中添加额外逻辑。请参见:

5. 测试和验证

验证事件

从您的应用或后端触发自定义事件并确认。 在 OneSignal 中,转到 分析 > 自定义事件 并确认您看到:
  • 为您的外部 ID 显示 booking_started 事件
  • 为您的外部 ID 显示 booking_complete 事件

验证数据源

使用已知用户 ID 手动调用您的数据源端点并确认:
  • 返回 200 响应
  • 存在所有预期字段

验证邮件

从 Journey 编辑器发送测试消息并确认:
  • 预订邮件包含真实预订详情
  • 恢复邮件包含有效优惠券
  • 没有 Liquid 变量显示为空
如果个性化缺失,请确认数据源请求中的用户 ID 与触发 Journey 的用户匹配。

示例:数据源实现

此示例显示了预订确认和恢复数据源的最小 Express 实现。您的后端语言、框架和数据源可以不同,只要 JSON 响应结构与您的邮件模板匹配即可。

预订数据源示例

import express from "express";

const app = express();

function dataFeedAuth(req, res, next) {
  if (req.headers["x-api-key"] !== process.env.DATAFEED_API_KEY) {
    return res.status(401).json({ error: "Unauthorized" });
  }
  next();
}

app.get("/datafeed/booking", dataFeedAuth, async (req, res) => {
  const { user_id } = req.query;

  if (!user_id) {
    return res.status(400).json({ error: "Missing user_id" });
  }

  const booking = await getLatestBookingForUser(user_id);

  if (!booking) {
    return res.status(404).json({ error: "No booking found" });
  }

  res.json({
    first_name: booking.first_name,
    last_booking: {
      service_type: booking.service_type,
      booking_date: booking.booking_date,
      booking_time: booking.booking_time,
      price: booking.price
    }
  });
});

优惠券数据源示例

app.get("/datafeed/coupon", dataFeedAuth, async (req, res) => {
  const { user_id } = req.query;

  if (!user_id) {
    return res.status(400).json({ error: "Missing user_id" });
  }

  const coupon = await generateCouponForUser(user_id);

  res.json({
    first_name: coupon.first_name,
    code: coupon.code,
    discount_text: coupon.discount_text,
    expires_in_hours: coupon.expires_in_hours,
    deep_link: coupon.deep_link
  });
});

实现指南

  • 保持响应快速(数据源在发送时被调用)
  • 始终返回可预测的 JSON 结构
  • 当没有数据存在时使用 404
  • 通过请求标头发送的 API 密钥保护端点

常见问题

邮件显示空值

  • 数据源返回 404
  • JSON 响应中的字段名称已更改
  • 用户身份不匹配

Journey 不分支

  • 未跟踪 booking_complete 事件
  • 事件名称不匹配(区分大小写)
  • 事件发生在等待窗口之外

数据源返回 401 或 403

  • API 密钥缺失或无效
  • 标头未在数据源设置中配置

下一步

  • 添加事件属性(服务类型、价格)以实现更高级的 Journey 条件
  • 添加额外的恢复步骤,如推送或短信提醒
  • 使用 Journey 出口规则防止重复恢复消息