PAYATHON 2026

美国运营商 Premium SMS 月度订阅计费方案

支付小周

结论

传统的美国 Premium SMS 月度订阅计费模式,通常已经不再适合作为通用方案。美国主要运营商早已大幅收紧或停止向普通第三方内容商开放 Premium SMS 计费。常见的 SMS aggregator 即使支持短码、PIN 验证和短信发送,通常也无法将费用计入用户的手机账单。

如果业务必须通过运营商账单收费,需要寻找支持美国市场的 Direct Carrier Billing(DCB,运营商直接计费)服务商,并确认其具备以下能力:

  • 覆盖目标美国运营商;
  • 支持 recurring subscription,而不只是单次购买;
  • 支持 Web opt-in;
  • 支持 PIN 或短信确认;
  • 提供 PHP 可调用的 HTTP API;
  • 支持内容分发、退订和退款流程;
  • 负责运营商代收及结算。

能否接入,通常取决于内容类型、企业资质、预计交易量和运营商审核。市场上并不存在注册后就能直接使用的统一 API。如果 DCB 服务商无法承接该业务,更可行的做法是通过信用卡、Apple Pay、Google Pay 等方式收费,再使用合规的 SMS 平台发送内容。

为什么普通 SMS aggregator 无法解决计费问题

这类业务包含三项不同的能力:

  1. 发送短信:通过 SMS API、短码、10DLC 或 toll-free number 发送消息。
  2. 验证订阅:发送 PIN 或要求用户回复 YES,完成 double opt-in。
  3. 运营商代扣费:将订阅费计入用户的移动电话账单。

Twilio、Sinch、Infobip 等通信平台可能支持前两项,但是否支持美国运营商账单代扣、月度订阅以及特定内容,需要逐项确认。平台提供短信 API,并不代表它也支持 Premium SMS billing。

美国的 Premium SMS 也不是简单地“发送一条高价短信”。计费能力必须由运营商或其授权计费合作方开放,业务、内容、用户流程和合规方案都要经过审核。

推荐的接入流程

1. 先确认业务是否符合准入要求

联系候选 DCB 服务商时,应提供以下信息:

  • 公司注册地和经营主体;
  • 网站及隐私政策;
  • 具体内容类型;
  • 月费金额和计费周期;
  • 目标运营商及用户范围;
  • 预计订阅量和交易额;
  • 客服、退款及投诉处理方式;
  • 完整的 opt-in、续费和退订页面原型。

还应直接询问:

Do you support recurring direct carrier billing in the United States?
Which US mobile carriers are supported?
Do you support web-based PIN or SMS double opt-in?
Is the charge placed directly on the subscriber's mobile phone bill?
Do you provide REST API callbacks for activation, renewal, cancellation and refund?
What compliance review and minimum volume requirements apply?

“支持美国”不一定意味着覆盖所有运营商,“支持 carrier billing”也不一定包括 recurring billing。接入前必须取得明确的运营商覆盖表和订阅能力说明。

2. 设计双重确认流程

典型流程如下:

  1. 用户在网页上输入手机号码。
  2. 后端创建一条待确认的订阅请求。
  3. 计费服务商识别号码所属运营商,并发送一次性 PIN。
  4. 用户在网页上提交 PIN。
  5. 服务商验证 PIN,并向用户展示价格、计费周期和退订方式。
  6. 订阅激活后,服务商通过 webhook 通知应用。
  7. 应用收到可信的激活事件后,才向用户开放内容。
  8. 此后的续费成功、续费失败、退订和退款,都通过 webhook 同步状态。

页面必须明确展示月费、扣费频率、短信费用说明、客服信息和取消方式,不能只把这些内容放在服务条款里。

PHP 接入示例

不同服务商使用的端点、签名方式和字段差异很大。以下代码只展示一种安全的集成结构。API_BASE_URL、请求字段和事件名称都必须替换为签约服务商的官方定义。

创建 PIN 验证请求

<?php

$apiBaseUrl = getenv('DCB_API_BASE_URL');
$apiKey = getenv('DCB_API_KEY');

$phoneNumber = $_POST['phone_number'] ?? '';

if (!preg_match('/^\+1[2-9]\d{9}$/', $phoneNumber)) {
    http_response_code(422);
    exit('Invalid US phone number');
}

$payload = [
    'phone_number' => $phoneNumber,
    'product_id' => 'monthly_content',
    'callback_url' => 'https://example.com/webhooks/dcb'
];

$ch = curl_init($apiBaseUrl . '/subscription-intents');

curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json'
    ],
    CURLOPT_POSTFIELDS => json_encode($payload, JSON_THROW_ON_ERROR),
    CURLOPT_TIMEOUT => 15
]);

$responseBody = curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

if ($responseBody === false) {
    throw new RuntimeException(curl_error($ch));
}

curl_close($ch);

if ($statusCode < 200 || $statusCode >= 300) {
    error_log($responseBody);
    http_response_code(502);
    exit('Unable to start subscription');
}

$response = json_decode($responseBody, true, 512, JSON_THROW_ON_ERROR);

// 将 intent_id 保存在服务端会话或数据库中,不要信任客户端传回的产品和价格。
$_SESSION['subscription_intent_id'] = $response['intent_id'];

提交 PIN

<?php

$apiBaseUrl = getenv('DCB_API_BASE_URL');
$apiKey = getenv('DCB_API_KEY');

$intentId = $_SESSION['subscription_intent_id'] ?? '';
$pin = $_POST['pin'] ?? '';

if (!preg_match('/^\d{4,8}$/', $pin)) {
    http_response_code(422);
    exit('Invalid PIN');
}

$payload = ['pin' => $pin];

$ch = curl_init(
    $apiBaseUrl . '/subscription-intents/' .
    rawurlencode($intentId) . '/confirm'
);

curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json'
    ],
    CURLOPT_POSTFIELDS => json_encode($payload, JSON_THROW_ON_ERROR),
    CURLOPT_TIMEOUT => 15
]);

$responseBody = curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

if ($statusCode < 200 || $statusCode >= 300) {
    http_response_code(400);
    exit('PIN confirmation failed');
}

// 不要仅凭这个同步响应授予长期访问权限。
// 最终订阅状态应以经过签名验证的 webhook 为准。
echo 'Confirmation received';

验证 webhook

<?php

$webhookSecret = getenv('DCB_WEBHOOK_SECRET');
$rawBody = file_get_contents('php://input');
$receivedSignature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';

$expectedSignature = hash_hmac('sha256', $rawBody, $webhookSecret);

if (!hash_equals($expectedSignature, $receivedSignature)) {
    http_response_code(401);
    exit('Invalid signature');
}

$event = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);
$eventId = $event['id'] ?? null;
$eventType = $event['type'] ?? null;

// 先按 eventId 做幂等检查,避免重复 webhook 导致重复处理。
switch ($eventType) {
    case 'subscription.activated':
        // 标记订阅有效并开放内容。
        break;

    case 'subscription.renewed':
        // 更新下一计费周期或有效期。
        break;

    case 'subscription.payment_failed':
        // 暂停服务或进入宽限期。
        break;

    case 'subscription.cancelled':
    case 'subscription.refunded':
        // 停止后续内容发送并更新订阅状态。
        break;
}

http_response_code(204);

签名请求头、HMAC 算法和事件名称仅供示意,实现时必须严格遵循服务商文档。如果服务商采用时间戳签名,还要检查时间窗口,防止重放攻击。

合规和运营注意事项

明示价格与周期

用户确认订阅前,应清楚看到:

  • 每月收费金额;
  • 是否自动续订;
  • 内容和消息发送频率;
  • Message and data rates may apply 等必要提示;
  • 退订方法,例如回复 STOP;
  • 获取帮助的方法,例如回复 HELP;
  • 客服联系方式;
  • 隐私政策和服务条款。

保存可审计的同意记录

至少应保存:

  • 用户提交的手机号码;
  • 页面和条款版本;
  • 同意时间;
  • PIN 请求及确认结果;
  • IP 地址和 User-Agent;
  • 服务商返回的 transaction ID;
  • 激活、扣费、失败、退款和退订事件。

不要保存明文 PIN,也不要把 API 密钥放进浏览器端代码。

正确处理退订

用户通过网页、客服或短信发送 STOP 后,应及时停止内容发送,并取消后续计费。只停止短信却不取消计费,或者只取消计费却继续发送营销消息,都会增加投诉风险。

号码不能等同于身份

手机号可能被回收,也可能转移给其他用户。系统需要处理号码停用、携号转网、运营商变更和订阅状态失效,不能永久将手机号视为同一个人的可靠身份标识。

评估实际收入

运营商计费通常涉及服务商分成、运营商分成、退款、拒付、税费和结算周期。签约前应确认:

Net revenue =
Gross charges
- carrier share
- provider fees
- refunds
- taxes
- bad debt adjustments

具体比例不能沿用历史 Premium SMS 项目的经验值,应以当前合同和目标运营商政策为准。

不可行时的替代方案

如果找不到既覆盖美国运营商、又接受该内容类型的 recurring DCB 服务商,可以将支付与内容发送拆开处理:

  • 使用 Stripe Checkout、PayPal 或其他支付平台管理月度订阅;
  • 使用短信服务商完成号码验证和内容发送;
  • 通过支付 webhook 控制订阅状态;
  • 在短信中提供统一的 STOP 和 HELP 处理。

这种方案无法把费用计入手机账单,但覆盖范围更广,API 也更成熟,更容易准确处理续费、退款和税务。对大多数面向美国用户的新项目来说,这通常比继续寻找传统 Premium SMS aggregator 更现实。

备注:内容仅供参考。