remp/campaign-module

REMP Campaign Laravel 包


README

活动管理器允许您在不了解HTML/CSS/JS的情况下创建横幅活动,对横幅进行A/B测试,并结合REMP Beam(用于提供统计信息)显示和评估活动的性能。

CMS/CRM集成

JavaScript代码片段

将以下片段包含到页面中,以处理活动并显示横幅。根据需要更新rempConfig对象。

注意:要自动跟踪横幅事件到BEAM Tracker,请向rempConfig对象中添加tracker属性。有关详细信息,请参阅BEAM README。这两个片段相互补充,可以合并成一个包含活动和Beam功能的JS代码片段。

(function(win, doc) {
    function mock(fn) {
        return function() {
            this._.push([fn, arguments])
        }
    }
    function load(url) {
        var script = doc.createElement("script");
        script.type = "text/javascript";
        script.async = true;
        script.src = url;
        doc.getElementsByTagName("head")[0].appendChild(script);
    }
    win.remplib = win.remplib || {};
    var mockFuncs = {
        "campaign": "init",
        "tracker": "init trackEvent trackPageview trackCommerce",
        "iota": "init"
    };

    Object.keys(mockFuncs).forEach(function (key) {
        if (!win.remplib[key]) {
            var fn, i, funcs = mockFuncs[key].split(" ");
            win.remplib[key] = {_: []};

            for (i = 0; i < funcs.length; i++) {
                fn = funcs[i];
                win.remplib[key][fn] = mock(fn);
            }
        }
    });
    // change URL to location of CAMPAIGN remplib.js
    load("http://campaign.remp.press/assets/lib/js/remplib.js");
})(window, document);

var rempConfig = {
    // UUIDv4 based REMP BEAM token of appropriate property
    // (see BEAM Admin -> Properties)
    // required if you're using REMP segments
    token: String,
    
    // optional, identification of logged user
    userId: String,
    
    // optional, flag whether user is currently subscribed to the displayed content 
    userSubscribed: Boolean,
    
    // optional, this is by default generated by remplib.js library and you don't need to override it
    browserId: String,
    
    // optional, controls where cookies (RTM - REMP's UTM parameters of visit) are stored
    cookieDomain: ".remp.press",

    // optional, controls which type of storage should be used by default (`local_storage` or `cookie`)
    // default is `local_storage`
    storage: "local_storage", 

    // optional, specifies how long to store specific keys in storage (in minutes)
    storageExpiration: {
        // default value (in minutes) for all storage keys if not overriden in `keys`
        "default": 15,
        // specific pairs of key name and life length in minutes
        "keys": {
            "browser_id": 1051200, // 2 years in minutes
            "campaigns": 1051200
        }
    },

    // required, Campaign specific options
    campaign: {
        // required, URL host of REMP Campaign
        url: "http://campaign.remp.press",
        
        // Additional params that will be appended links within displayed banner
        //
        // Key represents variable name, value should be defined as callback returning string response.
        // Following example will be appended as "&foo=bar&baz=XXX".
        // If the value is not function, remplib validation will throw an error and won't proceed further.
        bannerUrlParams:  {
            "foo": function() { return "bar" },
            "baz": function() { return "XXX" }
        },
        
        variables: {
            // variables replace template placeholders in banners,
            // e.g. {{ email }} -> foo@example.com
            //
            // the callback doesn't pass any parameters, it's required for convenience and just-in-time evaluation
            //
            // missing variable is translated to empty string
            email: {
                value: function() {
                    return "foo@example.com"
                }
            },
        },
        
        // Optional. Pageview attributes are used to provide to Campaign additional information about the pageview.
        // You can configure your campaigns to be displayed based on these attributes - see "Pageview attributes"
        // section when editing the campaign.
        //
        // The value can be:
        //   - string: Campaign is displayed when the string matches configured value for given attribute name.
        //   - array of strings: Campaign is displayed, when one of the provided strings matches the configured value.
        //
        // Any other kind of value is ignored and triggers JS console warning.
        //
        // All of provided attributes which are not configured in the campaign are ignored during processing.
        pageviewAttributes: {
            "attribute_1": "value_1",
            "attribute_2": ["value_1", "value_2"]
        }
    }
    
    // if you want to automatically track banner events to BEAM Tracker,
    // add also rempConfig.tracker property
    //
    // see REMP BEAM README.md
    
};
remplib.campaign.init(rempConfig);
单页应用程序

如果您使用单页应用程序并且需要在加载后重新初始化JS库

  1. 更新rempConfig变量以反映导航更改。
  2. 再次调用remplib.campaign.init(rempConfig)以重新初始化JS跟踪状态。所有现有的横幅将隐藏,活动将再次评估。

Segment集成

为了确定向谁显示活动,活动依赖于用户段(实际上是一系列应看到横幅的用户/浏览器ID)。您可以注册尽可能多的段提供者,唯一条件是提供者应与同一用户基础(一个用户ID必须始终指向)相同用户。

实现必须实现App\Contracts\SegmentContract接口。

所有注册的实现都隐藏在SegmentAggregator的伪装之下。然后使用该伪装显示配置列表中的可用段,并在活动运行时评估段的实际成员。

如果您想将活动链接到自己的系统,以下是实现的方法

  • provider(): string:在段提供者中唯一标识段提供者。这在内部分配段名称时需要,以防在多个段源中使用相同的段名称。

    return "my-provider"; 
  • list(): Collection:返回此提供者所有可用段的集合。响应结构为

    return [
        [
            'name' => String, // user friendly label
            'provider' => String, // should be same as result of provider()
            'code' => String, // machine friendly name, slug
            'group' => [
                'id' => Integer, // ID of segment group
                'name' => String, // user friendly label of group
                'sorting' => Integer // sorting index; lower the number, sooner the group appears in the list 
            ]
        ],
    ];
  • users(CampaignSegment $segment): Collection:返回属于该段的用户ID列表。

    • $segment:包含用于活动引用的CampaignSegment实例。

    响应预期为表示用户ID的整数/字符串集合

    return collect([
        String,
        String,
        // ...
    ])
  • checkUser(CampaignSegment $campaignSegment, string $userId): bool:检查给定的userId是否属于提供的campaignSegment。这可以通过外部API(如果已准备用于实时使用)或用户ID的缓存列表完成——这是实现内部的部分。如果实现不支持基于用户的段,则返回false

  • checkBrowser(CampaignSegment $campaignSegment, string $browserId): bool:检查给定的browserId是否属于提供的campaignSegment。这可以通过外部API(如果已准备用于实时使用)或浏览器ID的缓存列表完成——这是实现内部的部分。如果实现不支持基于浏览器的段,则返回false

  • cacheEnabled(): bool:标志是否应该由应用程序缓存此提供者中用于活动活动的段。如果为true,则活动每小时将请求每个活动中的users()并存储用户ID集合到应用程序缓存中。

  • getProviderData():提供者可以将任意数据传递回用户的浏览器。这可以用来缓存与浏览器/用户相关的信息,而不影响后端系统。这些数据可以随后由前端应用程序中的JS(如有必要)进行修改。所有提供的数据都在每次campaign/showtime请求时传递回服务器 - 每次横幅显示尝试时。

    注意:我们使用这种缓存来直接在浏览器中计算可跟踪事件的数量。然后根据缓存中提供的数据评估基于这些事件的段规则,而不是击中数据库。这种方法大大提高了REMP Beam事件数据库(Elasticsearch)的性能。

  • setProviderData($providerData): void:用于存储由前端应用程序在传入请求中提供的数据的辅助providerData方法。

实现时,创建一个提供您实现实例的段提供者。提供者应将\App\Contracts\SegmentAggregator::TAG设置为类,以便您的实现也能注册到SegmentAggregator

横幅自定义样式

横幅使用默认系统字体。如果您想使用自己的字体,您可以在自定义CSS字段中添加对.remp-banner .serif.remp-banner .sans-serif的样式,或者直接使用横幅显示功能将它们添加到网页中。

示例

.remp-banner .serif {
    font-family: 'Lumin Serif', serif;
}

.remp-banner .sans-serif {
    font-family: 'Lumin Sans', sans-serif;
}

设置和功能

活动位置冲突解决(实验性)

默认情况下,活动将所有活动匹配的横幅显示在其预期显示的位置。如果您想确保始终只有一个横幅在单个位置显示,您可以通过ENV变量启用此功能。

PRIORITIZE_BANNERS_ON_SAME_POSITION=1

当此功能启用且系统在相同位置解析多个横幅时,规则如下

  • 显示属于具有最多变体的活动的横幅。
  • 如果有更多,显示最近更新的活动的横幅。
可折叠横幅状态存储

可折叠横幅存储其折叠状态,并在下次显示时以相同状态显示(展开、折叠)。如果用户折叠活动横幅,则下一次显示时将折叠显示,反之亦然。

在可折叠横幅设置中有一个切换,可以覆盖此行为并始终以初始状态显示横幅。

片段

活动支持在横幅模板内容、自定义JavaScript和自定义CSS中使用片段。您可以在“片段”主菜单部分使用“添加新片段”创建片段。

您可以通过将{{ snippet_name }}添加到用于横幅内容或自定义JavaScript & CSS的其中一个字段来使用创建的片段。

活动调试器

为了能够调试实时活动和解决问题,活动提供活动调试器工具。

在活动的.env文件中设置CAMPAIGN_DEBUG_KEY。它可以是一个(最好是随机生成的)值 - 当您使用调试器时,您将被要求输入密钥。

要启动调试器,转到显示您想要调试的活动所在的页面,并在URL中附加#campaignDebug。在调试器窗口中,填写所需的参数Debug keyCampaign public ID,然后提交。页面刷新后,调试器应提供显示或未显示特定活动的理由。

API 文档

活动为以下描述的任务提供简单的API。

所有示例都使用http://campaign.remp.press作为基础域名。请在执行示例之前将主机更改为您使用的域名。

所有示例使用 XXX 作为默认的授权令牌,请将其替换为在 REMP SSO 中获得的真实令牌 API 令牌。

所有请求应包含(并符合)以下 HTTP 头。

Content-Type: application/json
Accept: application/json
Authorization: Bearer REMP_SSO_API_TOKEN

API 响应可以包含以下 HTTP 状态码

如果可能的话,响应将包含带有进一步错误信息的 application/json 编码的有效负载。

POST /api/banners/BANNER_ID/one-time-display

活动支持对特定用户一次性显示横幅。此类横幅不需要关联活动即可显示。这可以用作提醒用户执行所需操作的方式(而不是通过电子邮件通知他们)。

调用此端点时,请将 URL 中的 BANNER_ID 替换为实际的横幅 ID。每个一次性横幅都必须指定其到期日期和要显示给的用户(通过用户 ID 或浏览器 ID 进行定位)。

请求体
{
  "user_id": "29953", // String; Required (if browser_id is not present); ID of targeted user
  "browser_id": "aaaaaa-bbbb-cccc-dddd-1111111", // String; Required (if user_id is not present); ID of targeted browser
  "expires_at": "2019-12-29T10:05:00" // Date; Required; RFC3339 formatted datetime, specifying when the one-time banner expires (it won't show after the date)
}
示例:
curl
curl -X POST \
  http://campaign.remp.press/api/banners/[BANNER_ID]/one-time-display \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer XXX' \
  -H 'Content-Type: application/json' \
  -d '{
        "user_id": "29953",
        "expires_at": "2019-12-29T10:05:00"
}'
原始 PHP
$payload = [
    "user_id" => "29953",
    "expires_at" => "2019-12-29T10:05:00"
];
$jsonPayload = json_encode($payload);
$context = stream_context_create([
        'http' => [
            'method' => 'POST',
            'header' => "Content-Type: type=application/json\r\n"
                . "Accept: application/json\r\n"
                . "Content-Length: " . strlen($jsonPayload) . "\r\n"
                . "Authorization: Bearer XXX",
            'content' => $jsonPayload,
        ]
    ]
);
$bannerId = 1;
$response = file_get_contents("http://campaign.remp.press/api/banners/{$bannerId}/one-time-display", false, $context);
// process response (raw JSON string)
响应

有效的 202 HTTP 状态码的响应

{
  "status": "ok"
}
覆盖段缓存中用户的定位
  • 添加用户: POST /api/segment-cache/provider/{segment_provider}/code/{segment_code}/add-user
  • 删除用户: POST /api/segment-cache/provider/{segment_provider}/code/{segment_code}/remove-user

如果段提供者支持,活动将缓存段成员以实现快速访问。如果段是动态的(经常更改),可能会导致活动定位错误(旧数据)。

注意:缓存失效间隔设置为每小时一次(检查 App\Console\Kernel->schedule())。

这两个 API 端点允许从活动外部覆盖缓存段中一个用户的成员资格(例如,从 CRM)。

如果段未被活跃或计划中的活动积极使用,则两个端点都会返回响应 404 Not found

API 路径参数
请求体
{
  "user_id": "29953", // String; Required; ID of targeted user
}
示例:
curl

路径参数

  • segment_provider - crm_segment
  • segment_code - example_testing_segment
curl -X POST \
  http://campaign.remp.press/api/segment-cache/provider/crm_segment/code/example_testing_segment/add-user \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer XXX' \
  -H 'Content-Type: application/json' \
  -d '{
        "user_id": "29953"
}'
原始 PHP
$payload = [
    "user_id" => "29953",
];
$jsonPayload = json_encode($payload);
$context = stream_context_create([
        'http' => [
            'method' => 'POST',
            'header' => "Content-Type: type=application/json\r\n"
                . "Accept: application/json\r\n"
                . "Content-Length: " . strlen($jsonPayload) . "\r\n"
                . "Authorization: Bearer XXX",
            'content' => $jsonPayload,
        ]
    ]
);
$segmentProvider = "crm_segment";
$segmentCode = "example_testing_segment";
$response = file_get_contents("http://campaign.remp.press/api/segment-cache/provider/{$segmentProvider}/code/{$segmentCode}/add-user", false, $context);
// process response (raw JSON string)
响应

有效的 202 HTTP 状态码的响应

{
  "status": "ok"
}

404 HTTP 状态码的响应

{
  "status": "error",
  "message": "Segment with code [example_testing_segment] from provider [crm_segment] is not actively used in the campaign."
}

新闻通讯横幅

此横幅类型允许您直接收集新闻通讯订阅者。

在大多数情况下,您需要自己的 API 代理,因为大多数新闻通讯 API(包括 REMP CRM)都使用不应向客户端公开的私有令牌。如果您的新闻通讯 API 不需要这样的令牌,您可能不需要代理。

设置

所有配置选项都通过 .env 设置,有关可用选项的完整列表,请参阅 .env.example

API 要求

新闻通讯 API 必须以正确的 HTTP 响应代码响应。在成功和失败的情况下,都会发出自定义事件。有关详细信息,请参阅下一节。

自定义事件

如果请求配置了 XHR,则可用的自定义事件为 rempNewsletterSubscribeFailurerempNewsletterSubscribeSuccess。事件在 form 元素上触发。事件在 detail 属性中包含以下项

错误处理示例
window.addEventListener("rempNewsletterSubscribeFailure", function(event){
    if (event.detail && event.detail.type === 'response'){
        const response = event.detail.response;
        console.warn('HTTP Status Code:', response.status);
        
        // to retrieve body use `json()` or `text()` depending on your API implemetation
        response.json().then(function(data) {
            console.warn(data);
        });
    }

    if (event.detail && event.detail.type === 'exception') {
        console.warn(event.detail.message);
    }
});

健康检查

路由 http://campaign.remp.press/health 提供数据库、Redis、存储和日志的健康检查。

返回

  • 200 OK 和包含服务列表的 JSON(状态为 "OK")。

  • 500 Internal Server Error 和包含问题描述的 JSON。例如

    {
      "status":"PROBLEM",
      "log":{
        "status":"PROBLEM",
        "message":"Could not write to log file",
        "context": // error or thrown exception...
      //...
    }
    

MaxMind - GeoIP2 Lite

此产品包含由 MaxMind 创建的 GeoLite2 数据,可在 http://www.maxmind.com 获取。由于许可证变更,您需要手动更新数据库。如果这样做,请通过 MAXMIND_DATABASE 环境变量提供更新数据库的路径。

RTM 跟踪

以下参数添加到每个请求或横幅内的链接中。它们用于活动性能跟踪。

  • rtm_source: "remp_campaign" => 始终相同的字符串
  • rtm_medium: displayType => 覆盖或内嵌
  • rtm_campaign: campaignUuid => 广告活动 ID
  • rtm_content: uuid => 横幅 ID
  • banner_variant: variantUuid => 横幅变体 ID(在广告活动中)