哪里有开发 opencart payment method 的详细文档?
结论
OpenCart 并没有一套适用于所有版本且内容完整的 Payment Method 开发手册。更可靠的学习方法是先确定项目使用的 OpenCart 版本,再以官方 GitHub 源码和同版本的内置支付扩展为主要参考,同时借助官方开发文档、社区论坛和 Stack Overflow 解决具体问题。
第三方教程有助于理解开发流程,但不适合直接复制代码。OpenCart 2.x、3.x 和 4.x 在目录结构、类名、命名空间、模板系统和扩展安装方式上都有明显差异。
推荐的参考资料
建议按以下顺序查阅:
-
OpenCart 官方 GitHub 仓库
这是最准确的参考来源。先切换到与项目版本一致的 tag 或分支,再查看已有支付扩展的实现。 -
OpenCart 官方开发文档
可用于了解 MVC-L、路由、语言文件、模板和扩展的基本结构。不过,部分内容可能没有覆盖最新版本。 -
OpenCart Community Forum
适合查询特定版本的兼容问题,以及订单状态处理、回调通知和扩展安装错误。 -
Stack Overflow 的 OpenCart 相关问题
适合按报错信息或具体开发问题搜索。查看回答时,需要留意发布日期和适用版本。 -
同版本的成熟支付扩展
研究 PayPal、银行转账或货到付款等现有扩展,通常比阅读零散教程更有效。具体应以当前项目源码中实际存在的扩展为准。
开发前先确认版本
不要只看后台页面显示的版本号,还要核对项目源码。不同大版本的典型差异如下:
| 项目 | OpenCart 2.x / 3.x | OpenCart 4.x |
|---|---|---|
| 类组织方式 | 传统类名,如 ControllerExtensionPayment... | 广泛使用命名空间 |
| 模板 | 2.x 可能使用 .tpl,3.x 通常使用 Twig | 使用新版扩展结构和 Twig |
| 扩展目录 | 常见于 extension/payment | 目录和安装包组织方式不同 |
| 修改机制 | 可能使用 OCMOD | 扩展事件和安装机制有所调整 |
| 路由格式 | 传统 route 写法 | 控制器调用方式有所变化 |
因此,针对 OpenCart 3.x 编写的教程不能直接作为 OpenCart 4.x 的开发文档使用。
Payment Method 的基本组成
以常见的 OpenCart 3.x 结构为例,支付扩展通常分为后台配置和前台结账两部分:
admin/
├── controller/extension/payment/example_pay.php
├── language/en-gb/extension/payment/example_pay.php
├── model/extension/payment/example_pay.php
└── view/template/extension/payment/example_pay.twig
catalog/
├── controller/extension/payment/example_pay.php
├── language/en-gb/extension/payment/example_pay.php
├── model/extension/payment/example_pay.php
└── view/theme/default/template/extension/payment/example_pay.twig
实际文件可能比这份列表少。例如,简单的支付方式未必需要后台 model。多语言商店则要为每种语言准备相应文件。
各部分的职责是:
- 后台 controller:读取并保存商户号、密钥、测试模式和订单状态等配置。
- 后台 language 和 view:构成支付方式的管理界面。
- 前台 model:根据当前地址、币种、金额或地区判断支付方式是否可用。
- 前台 controller:准备支付参数、创建签名、跳转到网关并处理返回结果。
- 前台 view:显示“确认支付”按钮或网关表单。
- 回调入口:接收支付平台的异步通知并更新订单状态。
OpenCart 3.x 的最小示例
下面的代码只用于说明扩展各层之间的调用关系,不包含真实支付网关所需的签名和安全校验。
前台 model:
<?php
class ModelExtensionPaymentExamplePay extends Model {
public function getMethod($address, $total) {
$status = true;
if (!$this->config->get('payment_example_pay_status')) {
$status = false;
}
if ($total <= 0) {
$status = false;
}
if (!$status) {
return [];
}
return [
'code' => 'example_pay',
'title' => $this->language->get('text_title'),
'terms' => '',
'sort_order' => $this->config->get(
'payment_example_pay_sort_order'
)
];
}
}
对应的语言文件:
<?php
$_['text_title'] = 'Example Pay';
前台 controller:
<?php
class ControllerExtensionPaymentExamplePay extends Controller {
public function index() {
$data['action'] = $this->url->link(
'extension/payment/example_pay/confirm',
'',
true
);
return $this->load->view(
'extension/payment/example_pay',
$data
);
}
public function confirm() {
$this->load->model('checkout/order');
$order_id = (int)$this->session->data['order_id'];
$order_info = $this->model_checkout_order->getOrder($order_id);
if (!$order_info) {
$this->response->addHeader(
$this->request->server['SERVER_PROTOCOL'] . ' 400 Bad Request'
);
return;
}
// 在这里创建网关请求、计算签名并返回跳转地址。
// 不要把私钥或完整支付凭据发送到浏览器。
$json = [
'redirect' => $this->url->link(
'checkout/success',
'',
true
)
];
$this->response->addHeader('Content-Type: application/json');
$this->response->setOutput(json_encode($json));
}
}
Twig 模板:
<div class="buttons">
<div class="pull-right">
<button type="button" id="button-confirm" class="btn btn-primary">
{{ button_confirm }}
</button>
</div>
</div>
<script>
$('#button-confirm').on('click', function () {
$.ajax({
url: '{{ action }}',
dataType: 'json',
beforeSend: function () {
$('#button-confirm').prop('disabled', true);
},
complete: function () {
$('#button-confirm').prop('disabled', false);
},
success: function (json) {
if (json.redirect) {
location = json.redirect;
}
}
});
});
</script>
这段代码只能演示调用链,不能直接用于线上支付。真正的支付模块必须在服务器端验证金额、币种、订单号和网关签名。
建议的开发步骤
1. 找到同版本的参考扩展
从项目当前源码中选择一种流程相近的支付方式:
- 线下付款:参考银行转账或货到付款。
- 跳转支付:参考跳转到第三方收银台的扩展。
- API 支付:参考由服务器创建支付订单的扩展。
- 异步支付:重点查看通知回调和订单历史的更新逻辑。
不要把 master 分支的代码直接复制到旧版本项目中。应先切换到对应的 release tag。
2. 先实现支付方式展示
先确保扩展可以:
- 在后台安装并启用;
- 正确保存配置;
- 根据币种、地区和订单金额返回
getMethod(); - 出现在 checkout 的支付方式列表中。
完成这些功能后,再接入真实扣款。
3. 接入支付网关
按照网关文档实现以下功能:
- 创建支付请求;
- 服务端签名;
- 浏览器跳转或 API 调用;
- 处理同步返回;
- 处理异步通知;
- 查询订单和调用退款接口。
网关 SDK 的版本、请求字段和签名算法,应以支付平台当前文档为准。
4. 正确更新订单状态
支付成功后,通常需要通过订单 model 写入订单历史。具体调用参数应参照当前 OpenCart 版本中的 catalog/model/checkout/order.php,不能根据其他版本的示例推断。
处理逻辑需要区分以下情况:
- 用户完成付款;
- 用户取消付款;
- 支付处理中;
- 支付失败;
- 网关退款;
- 重复回调。
5. 使用沙箱测试完整链路
至少要测试以下场景:
- 正常支付;
- 支付取消;
- 金额或签名不一致;
- 异步通知早于页面返回;
- 同一通知重复发送;
- 用户付款后未返回商店;
- 不同币种和小数精度;
- 已支付订单再次收到通知。
上线时的关键注意事项
不要信任浏览器提交的数据
订单金额、币种、商品信息和订单状态都必须由服务器重新读取。浏览器传入的金额只能作为不可信数据处理。
回调必须验签
异步通知至少要验证以下内容:
- 签名是否合法;
- 商户号是否匹配;
- 网关订单号是否有效;
- 商户订单号是否对应本店订单;
- 实付金额和币种是否一致;
- 当前订单是否已经处理。
如果仅根据 status=success 更新订单,会产生明显的伪造支付风险。
回调处理要具备幂等性
支付平台可能重复发送同一条通知。模块应保存网关交易号,并在更新订单前检查当前状态,避免重复扣减库存、发送邮件或增加订单历史。
密钥只能保存在服务端
私钥、API Secret、Webhook Secret 等凭据不能写入 Twig、JavaScript、日志或公开仓库。日志中的银行卡号、令牌和个人信息也应隐藏。
不要混用不同版本的教程
遇到类不存在、路由找不到或模板无法加载时,应先检查教程适用的 OpenCart 版本。多数此类问题并不是支付接口出错,而是扩展结构与当前版本不匹配。
对于 OpenCart Payment Method 开发来说,对应版本的源码其实就是最完整的“文档”。先阅读内置支付扩展,再通过官方文档了解框架约定,最后根据支付网关文档完成签名、回调和退款流程,这套开发路径更可靠。
备注:内容仅供参考。
