PAYATHON 2026

如何获取 Sage Pay repeat payment API?

支付老李

结论

Sage Pay 的 repeat payment 通常不需要下载单独的 PHP API 或 SDK,而是向网关提交 REPEAT 类型的交易。你需要先完成一笔初始交易,并在交易成功后保存 Sage Pay 返回的交易标识和安全信息,用于发起后续扣款。

Sage Pay 现已更名为 Opayo。不同接入方式和协议版本使用的接口地址及字段要求可能不同,因此要先确认商户账户接入的是 Sage Pay Direct、Server,还是较新的 Opayo 接口。

工作原理

后续扣款需要关联一笔成功的原始交易。常用字段包括:

  • TxType=REPEAT
  • Vendor
  • VendorTxCode
  • Amount
  • Currency
  • Description
  • RelatedVPSTxId
  • RelatedVendorTxCode
  • RelatedSecurityKey

各字段的含义如下:

  • VendorTxCode 是本次扣款的新交易编号,每笔交易必须唯一。
  • RelatedVPSTxId 是初始交易返回的 VPSTxId。
  • RelatedVendorTxCode 是初始交易使用的 VendorTxCode。
  • RelatedSecurityKey 是初始交易返回的 SecurityKey。

所以,首次付款成功后不能只保存订单号,至少还要安全保存后续交易需要使用的关联字段。

PHP 请求示例

下面是传统 Sage Pay 协议的请求结构。$endpoint 需要按照实际接入产品、测试环境和协议版本填写,不能直接将示例地址用于生产环境。

<?php

$endpoint = getenv('SAGEPAY_REPEAT_ENDPOINT');

$data = [
    'VPSProtocol'         => '3.00',
    'TxType'              => 'REPEAT',
    'Vendor'              => 'your_vendor_name',
    'VendorTxCode'        => 'repeat_' . bin2hex(random_bytes(12)),
    'Amount'              => '19.99',
    'Currency'            => 'GBP',
    'Description'         => 'Monthly subscription',
    'RelatedVPSTxId'      => '{ORIGINAL_VPSTXID}',
    'RelatedVendorTxCode' => 'original_transaction_code',
    'RelatedSecurityKey'  => '{ORIGINAL_SECURITY_KEY}',
];

$ch = curl_init($endpoint);

curl_setopt_array($ch, [
    CURLOPT_POST            => true,
    CURLOPT_POSTFIELDS      => http_build_query($data, '', '&'),
    CURLOPT_RETURNTRANSFER  => true,
    CURLOPT_CONNECTTIMEOUT   => 10,
    CURLOPT_TIMEOUT          => 30,
    CURLOPT_SSL_VERIFYPEER   => true,
    CURLOPT_SSL_VERIFYHOST   => 2,
    CURLOPT_HTTPHEADER       => [
        'Content-Type: application/x-www-form-urlencoded',
    ],
]);

$response = curl_exec($ch);

if ($response === false) {
    throw new RuntimeException('Sage Pay request failed: ' . curl_error($ch));
}

$httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($httpStatus < 200 || $httpStatus >= 300) {
    throw new RuntimeException(
        'Sage Pay returned HTTP status ' . $httpStatus
    );
}

/*
 * 传统 Sage Pay 接口通常返回多行 key=value 内容,
 * 但应以当前协议文档规定的格式为准。
 */
$result = [];

foreach (preg_split('/\r\n|\r|\n/', trim($response)) as $line) {
    if ($line === '' || !str_contains($line, '=')) {
        continue;
    }

    [$key, $value] = explode('=', $line, 2);
    $result[trim($key)] = trim($value);
}

$status = $result['Status'] ?? null;

if ($status !== 'OK') {
    $detail = $result['StatusDetail'] ?? 'Unknown gateway error';
    throw new RuntimeException("Repeat payment failed: {$detail}");
}

echo 'Repeat payment accepted';

如果 PHP 版本低于 8.0,可以将:

str_contains($line, '=')

替换为:

strpos($line, '=') !== false

集成步骤

  1. 在商户后台确认账户是否支持 repeat payment 或连续扣款。
  2. 确认当前使用的 Sage Pay/Opayo 产品及协议版本。
  3. 查阅对应版本的官方协议文档,确认交易注册地址和必填字段。
  4. 在测试环境中完成一笔初始付款。
  5. 保存返回的 VPSTxId、VendorTxCode 和 SecurityKey。
  6. 创建新的唯一 VendorTxCode,提交 TxType=REPEAT 请求。
  7. 检查响应中的 Status 和 StatusDetail,不能只根据 HTTP 状态判断付款是否成功。
  8. 测试成功、失败、超时及重复提交等情况,确认无误后再切换到生产环境。

常见问题

为什么找不到 repeat payment 的源代码?

因为它通常是支付协议中的一种交易类型,并不是单独的软件包。PHP 端只需按照协议构造 HTTPS POST 请求。接入时主要需要处理字段、账户权限和交易状态。

可以只用订单号重复扣款吗?

通常不行。后续交易需要关联原始的支付网关交易,可能需要提交 RelatedVPSTxId、RelatedVendorTxCode 和 RelatedSecurityKey 等字段。

可以定时自动扣款吗?

网关只负责执行单次 REPEAT 请求,具体的扣款计划通常由你的系统管理。例如,可以使用队列或定时任务,在到期日生成新的唯一交易编号并调用接口。不要直接重试同一个请求,以免发生重复扣款。

能把银行卡号保存下来再提交吗?

不要自行保存完整卡号或 CVV。repeat payment 可以通过原交易引用完成后续扣款。保存和处理银行卡数据会显著扩大 PCI DSS 合规范围,而且 CVV 不应存储。

注意事项

  • 测试环境和生产环境的交易标识不能混用。
  • 金额应使用保留两位小数的十进制字符串,例如 19.99。
  • 每次后续扣款都要使用唯一的 VendorTxCode。
  • 发生网络超时时,不要立即重新扣款。应先查询交易状态,或按照官方建议处理。
  • 记录请求编号、响应状态和错误详情,但不要将安全字段写入普通日志。
  • repeat payment 并不是完整的订阅管理系统。账期、重试、退款和取消逻辑仍需由业务系统处理。
  • 某些账户、卡种、收单配置或认证规则可能不支持后续扣款。实际能力应以商户合同、当前协议文档和 Opayo 支持团队的确认为准。
  • 如果现有系统使用的是较新的 Opayo API,上述传统字段和请求格式可能不适用。此时应使用对应产品的 credential/token 或 recurring transaction 流程。

备注:内容仅供参考。