oscommerce PostFinance 集成显示 unknown order/1/s
结论
SHA-IN 和 SHA-OUT 使用相同的 passphrase 在技术上可行,但不建议这样配置。它们分别用于两个方向:
- SHA-IN:验证 osCommerce 发往 PostFinance 的请求。
- SHA-OUT:验证 PostFinance 返回给 osCommerce 的通知。
- osCommerce 后台填写的 SHA-IN 和 SHA-OUT passphrase,应分别与 PostFinance 后台的对应配置一致。
后台填写的是 passphrase(密钥),不是计算后得到的 SHASIGN。如果把固定的摘要值当作 signature 填入后台,配置就是错误的。
unknown order/1/s 也不一定是 SHA 配置造成的。它还可能表示 osCommerce 找不到回调中的订单号,或者返回地址、参数传递、字符编码、重写规则破坏了请求。
正确的配置关系
建议为两个方向设置不同的随机字符串:
PostFinance SHA-IN passphrase = osCommerce SHA-IN passphrase
PostFinance SHA-OUT passphrase = osCommerce SHA-OUT passphrase
不要把固定的 SHA 摘要填入对应字段:
SHA-IN field = 固定的 SHA 摘要
SHA-OUT field = 固定的 SHA 摘要
SHASIGN 需要根据每次请求的参数动态计算。SHA-IN 和 SHA-OUT 即使暂时使用同一个 passphrase,只要双方配置完全一致,通常也不会因此单独失败。不过,使用不同密钥更方便判断问题出在哪个方向,也符合密钥隔离原则。
检查 SHA 算法
PostFinance 后台选择的摘要算法必须与 osCommerce 模块使用的算法一致。常见选项包括:
SHA-1
SHA-256
SHA-512
如果 PostFinance 配置的是 SHA-512,而模块仍调用 sha1(),生成的 SHASIGN 一定无法通过验证。
旧版 osCommerce/PostFinance 模块可能只实现了 SHA-1。实际支持哪些算法取决于模块版本,不能只修改后台配置而不检查模块代码。
SHA-IN 的计算方式
传统 PostFinance/Ogone 集成通常不会直接执行:
sha1($queryString . $passphrase);
一般需要先按参数名排序,再在每个非空参数后追加 SHA-IN passphrase。例如:
<?php
$params = [
'AMOUNT' => '1999',
'CURRENCY' => 'EUR',
'LANGUAGE' => 'en_US',
'ORDERID' => '100123',
'PSPID' => 'your_pspid',
];
$shaInPassphrase = 'your_sha_in_passphrase';
uksort($params, 'strnatcasecmp');
$plainText = '';
foreach ($params as $name => $value) {
if ($value === '' || $value === null) {
continue;
}
$plainText .= strtoupper($name) . '=' . $value . $shaInPassphrase;
}
$shaSign = strtoupper(hash('sha256', $plainText));
这里的 sha256 只是示例,必须换成 PostFinance 后台实际选择的算法:
$shaSign = strtoupper(hash('sha1', $plainText));
$shaSign = strtoupper(hash('sha256', $plainText));
$shaSign = strtoupper(hash('sha512', $plainText));
还要确认模块只计算 PostFinance 规定参与签名的字段。加入本地字段、空字段或实际没有发送的字段,都可能造成签名不一致。
SHA-OUT 的验证方式
收到 PostFinance 回调后,应该使用 SHA-OUT passphrase 重新计算摘要,再与返回的 SHASIGN 比较:
<?php
$shaOutPassphrase = 'your_sha_out_passphrase';
$receivedShaSign = strtoupper($_POST['SHASIGN'] ?? '');
$params = $_POST;
unset($params['SHASIGN']);
uksort($params, 'strnatcasecmp');
$plainText = '';
foreach ($params as $name => $value) {
if ($value === '' || $value === null) {
continue;
}
// 实际项目中应只保留 PostFinance 规定参与 SHA-OUT 的字段
$plainText .= strtoupper($name) . '=' . $value . $shaOutPassphrase;
}
$calculatedShaSign = strtoupper(hash('sha256', $plainText));
if (!hash_equals($calculatedShaSign, $receivedShaSign)) {
throw new RuntimeException('Invalid PostFinance SHA-OUT signature');
}
不要直接对全部 $_POST 字段签名。不同接口版本允许参与 SHA-OUT 计算的字段可能不同,应以当前账户配置、模块实现和对应的接口文档为准。
排查 unknown order/1/s
1. 确认跳转前已经将订单写入数据库
有些模块在顾客跳转到 PostFinance 之前并未真正创建订单,回调时自然无法通过 ORDERID 查到记录。
先确认发送给 PostFinance 的订单号确实存在:
SELECT orders_id, customers_id, orders_status
FROM orders
WHERE orders_id = 100123;
如果 PostFinance 收到的订单号带有前缀或后缀,例如:
100123-1690000000
而回调代码直接使用整个字符串查询 orders_id,同样会出现 unknown order。发送订单号和解析订单号必须遵循同一套规则。
2. 记录原始回调参数
排查时可以记录参数名和订单号,但不要记录 passphrase、完整签名或支付卡数据:
error_log('PostFinance callback method: ' . $_SERVER['REQUEST_METHOD']);
error_log('PostFinance ORDERID: ' . ($_POST['orderID'] ?? $_GET['orderID'] ?? 'missing'));
error_log('PostFinance STATUS: ' . ($_POST['STATUS'] ?? $_GET['STATUS'] ?? 'missing'));
主要检查以下内容:
ORDERID是否存在;- 参数是通过
POST还是GET返回的; - 返回的订单号是否与发起支付时完全一致;
- osCommerce 模块读取的参数名大小写是否正确;
- 回调代码是否在验证签名前修改了参数值。
3. 检查返回 URL
PostFinance 后台配置的接受、拒绝、取消、异常和服务器通知 URL,都必须指向真正处理回调的 osCommerce 地址。
需要检查:
- HTTP 和 HTTPS 是否一致;
- 域名是否发生跳转;
- URL 是否经过登录页或 Cookie 检查;
- URL 重写是否删除了查询参数;
- 反向代理是否改变了请求方法;
- 动态回调地址是否被重定向到首页。
支付服务器通知不能依赖顾客浏览器中的 session。即使顾客关闭页面,服务器回调也应该能通过 ORDERID 独立找到订单。
4. 检查字符编码和参数规范化
计算 SHA 时使用的值必须与实际发送的值一致。以下任何变化都可能导致摘要不同:
空格与 +
& 与 &
UTF-8 与其他编码
大小写变化
URL 编码前后的值
金额的小数格式
换行符
不要先对参数调用 htmlspecialchars() 再计算 SHA。例如,参数值中的 & 一旦变成 &,签名也会随之改变。
5. 检查测试和生产环境
测试账户和生产账户通常使用独立配置。以下内容必须属于同一个环境:
- PSPID;
- SHA-IN passphrase;
- SHA-OUT passphrase;
- 摘要算法;
- 支付提交地址;
- 返回地址和服务器通知地址。
测试后台设置的 passphrase 不会自动同步到生产环境,不能假定两边配置相同。
推荐处理顺序
先在 PostFinance 后台分别设置新的 SHA-IN 和 SHA-OUT passphrase,再把对应值填入 osCommerce 模块,同时确认双方使用相同的摘要算法。然后创建一笔新的测试订单,记录发送的 ORDERID 以及回调收到的 ORDERID。
如果签名已经验证成功,但仍然出现 unknown order/1/s,接下来应检查订单创建时机、订单号解析和回调 URL,而不是继续反复修改 SHA 密钥。
备注:内容仅供参考。
