如何通过 FastSpring webhook 发送 siteURL 或 userID?
结论
可以。在调用 fastspring.builder.checkout() 前,使用 Store Builder 的 fastspring.builder.tag() 将站点标识、用户标识等自定义数据附加到订单。订单完成后,这些 tags 会随相关的 FastSpring webhook 事件返回。
Webhook 是 FastSpring 服务器单独发起的请求。它不知道购买者来自哪个 WordPress 站点,也没有对应站点的登录会话,所以无法在 webhook handler 中通过 get_current_user_id() 获取购买时的用户。这些信息需要在创建结账会话时传入,或者通过内部订单记录关联。
在 WordPress 页面中传入数据
先用 PHP 获取当前站点和用户信息,再安全地输出给 JavaScript:
<?php
$checkout_context = [
'siteURL' => home_url('/'),
'userID' => (string) get_current_user_id(),
];
?>
<script>
const checkoutContext = <?php echo wp_json_encode($checkout_context); ?>;
function dataPopupWebhookReceived(data) {
// 这是 Store Builder 的前端回调,不是服务器端 FastSpring webhook。
console.log('Store Builder event:', data);
}
function addVariationAndCheckout(variation) {
fastspring.builder.reset();
switch (variation) {
case 'pcm-monthly':
fastspring.builder.add('pcm-monthly');
break;
default:
return;
}
fastspring.builder.tag({
siteURL: checkoutContext.siteURL,
userID: checkoutContext.userID
});
fastspring.builder.checkout();
}
</script>
调用顺序必须是:
fastspring.builder.reset();
fastspring.builder.add('pcm-monthly');
fastspring.builder.tag({ /* custom data */ });
fastspring.builder.checkout();
reset() 会清除当前 Store Builder 会话中的数据,因此 tags 不能在 reset() 之前设置。
在 webhook handler 中读取
FastSpring 发送的 JSON 结构取决于 webhook 事件类型和当前接口格式。对于包含订单 tags 的事件,通常可以从事件或订单对象的 tags 字段读取,例如:
<?php
function handle_fastspring_webhook(WP_REST_Request $request) {
$payload = $request->get_json_params();
// 字段路径应以 FastSpring 实际发送的 webhook payload 为准。
$tags = $payload['tags'] ?? [];
$site_url = isset($tags['siteURL'])
? esc_url_raw($tags['siteURL'])
: null;
$user_id = isset($tags['userID'])
? absint($tags['userID'])
: 0;
// 根据订单号、站点标识和用户标识处理授权。
// 不要仅凭这些 tags 判定付款成功。
return new WP_REST_Response(['received' => true], 200);
}
如果实际 payload 中的 tags 位于嵌套对象中,就需要按照真实结构调整,例如:
$tags = $payload['order']['tags'] ?? [];
可以先记录一份经过敏感信息脱敏的测试 webhook payload,确认所订阅事件中 tags 的确切位置。
更稳妥的关联方式
直接传递 siteURL 和 WordPress userID 很方便,但不能将其作为可信的授权依据,因为浏览器端 JavaScript 可以被修改。更可靠的方式是生成一个不可预测的关联令牌:
<?php
$checkout_token = wp_generate_uuid4();
update_option(
'pcm_checkout_' . $checkout_token,
[
'site_url' => home_url('/'),
'user_id' => get_current_user_id(),
'created' => time(),
],
false
);
?>
<script>
const checkoutToken = <?php echo wp_json_encode($checkout_token); ?>;
fastspring.builder.tag({
checkoutToken: checkoutToken
});
</script>
Webhook 收到 checkoutToken 后,再从自己的数据库中查询对应的站点和用户,不要完全信任浏览器提交的 siteURL 或 userID。
注意事项
dataPopupWebhookReceived()是浏览器中的 Store Builder 回调,并不是发送到 WordPress REST API 的服务器端 webhook。- webhook 请求不包含 WordPress 登录 Cookie,因此其中的
get_current_user_id()通常返回0。 - 必须校验 FastSpring webhook 的签名或认证信息,并按照当前 FastSpring 配置使用相应的验证方式。
- 验证事件真实性、付款状态、产品 ID、订单号和金额后,才能发放授权。
- 不要将密码、访问令牌、私钥或其他敏感信息放入 tags。
userID只在单个 WordPress 安装中有意义。接入多站点或多个客户站点时,需要同时保存站点标识。最好使用后台生成的 installation ID,不要将可能变化的域名作为唯一主键。- webhook 可能重复发送。handler 应使用 FastSpring 事件 ID 或订单 ID 做幂等处理,避免重复创建许可证或重复开通服务。
备注:内容仅供参考。
