PAYATHON 2026

OpenCart 选择付款方式时添加银行账号必填字段

支付老李

结论

可以实现。从代码风格来看,这套结账流程应该属于 OpenCart 1.5.x:第 5 步通过 checkout/payment_method 选择付款方式,payment/incasso.tpl 通常要到第 6 步确认订单时才加载。

所以,如果把银行账号输入框放在 payment/incasso.tpl 中,它只能出现在第 6 步。要让输入框显示在第 5 步,需要:

  1. 在 checkout/payment_method.tpl 中添加一个默认隐藏的银行账号字段。
  2. 用户选择 incasso 时,通过 JavaScript 显示该字段。
  3. 在 checkout/payment_method 控制器中完成服务端校验。
  4. 校验通过后,将账号保存到 session。
  5. 在 payment/incasso/confirm 中读取 session,并将账号写入数据库。

下面的代码采用 OpenCart 1.5.x 的常见结构。具体方法名可能因小版本或自定义主题而异,一般是 validate() 或 save()。

在第 5 步添加输入框

编辑当前主题的文件:

catalog/view/theme/你的主题/template/checkout/payment_method.tpl

不要只修改 default 主题。如果商店使用的是其他主题,应当修改实际生效的模板。

在付款方式列表之后、Continue 按钮之前加入:

<div id="incasso-bank-account" style="display:none;">
    <label for="bankAccountNumber">
        Bank account number <span class="required">*</span>
    </label>

    <input
        type="text"
        name="bank_account_number"
        id="bankAccountNumber"
        value="<?php echo isset($bank_account_number)
            ? htmlspecialchars($bank_account_number, ENT_QUOTES, 'UTF-8')
            : ''; ?>"
        autocomplete="off"
    />
</div>

<script type="text/javascript">
function toggleIncassoBankAccount() {
    var selectedMethod = $('input[name="payment_method"]:checked').val();

    if (selectedMethod && selectedMethod.split('.')[0] === 'incasso') {
        $('#incasso-bank-account').show();
    } else {
        $('#incasso-bank-account').hide();
    }
}

$('input[name="payment_method"]').bind('change', function() {
    toggleIncassoBankAccount();
});

toggleIncassoBankAccount();
</script>

这里使用 split('.')[0],是因为部分 OpenCart 付款方式的值可能带有子方式,例如 module.method。如果单选框的值始终是 incasso,也可以直接比较。

标签和错误信息最好放进对应的语言文件,不要直接写死英文。

把已填写的内容传给模板

编辑:

catalog/controller/checkout/payment_method.php

在渲染 payment_method.tpl 前添加:

$this->data['bank_account_number'] = '';

if (isset($this->session->data['incasso_bank_account'])) {
    $this->data['bank_account_number'] =
        $this->session->data['incasso_bank_account'];
}

这样,客户返回上一步或重新加载付款方式区域时,之前填写的内容仍然会保留。

在第 5 步进行服务端校验

JavaScript 可以改善操作体验,但不能代替服务端校验。用户既可以关闭 JavaScript,也可以直接构造请求。

在 catalog/controller/checkout/payment_method.php 的 validate() 方法中,找到处理以下内容的位置:

$this->request->post['payment_method']

以及最终执行以下代码的位置:

if (!$json) {
    // 保存 payment_method
}

在最终保存前加入:

$selected_method = '';

if (isset($this->request->post['payment_method'])) {
    $selected_method = $this->request->post['payment_method'];
}

$method_parts = explode('.', $selected_method);
$method_code = $method_parts[0];

$bank_account_number = '';

if (isset($this->request->post['bank_account_number'])) {
    $bank_account_number = strtoupper(
        preg_replace(
            '/\s+/',
            '',
            trim($this->request->post['bank_account_number'])
        )
    );
}

if ($method_code == 'incasso') {
    if ($bank_account_number == '') {
        $json['error']['warning'] =
            'Please enter your bank account number.';
    } elseif (!preg_match('/^[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}$/', $bank_account_number)) {
        $json['error']['warning'] =
            'Please enter a valid IBAN.';
    }
}

随后,在原有的 if (!$json) 代码块中保存 session:

if (!$json) {
    // 保留 OpenCart 原有的 payment_method 保存代码。

    if ($method_code == 'incasso') {
        $this->session->data['incasso_bank_account'] =
            $bank_account_number;
    } else {
        unset($this->session->data['incasso_bank_account']);
    }
}

上面的正则表达式只检查 IBAN 的基本结构,不会验证 IBAN 校验位。正式环境中应使用完整的 IBAN Mod-97 校验器,也不能只依赖前端正则表达式。

如果当前版本使用的错误结构是:

$json['error'] = '...';

而不是:

$json['error']['warning'] = '...';

应当沿用现有控制器的格式,否则结账页面可能无法正常显示错误信息。

在确认订单时读取 session

现在的 confirm() 使用:

$rekNum = $_GET['rn'];

这种方式不适合传递银行账号。URL 查询参数可能被记录在浏览器历史、访问日志和统计系统中。

改为从 session 读取:

public function confirm() {
    $this->language->load('payment/incasso');

    $json = array();

    if (!isset($this->session->data['incasso_bank_account'])
        || $this->session->data['incasso_bank_account'] == '') {
        $json['error'] = 'Bank account number is missing.';

        $this->response->setOutput(json_encode($json));
        return;
    }

    $this->load->model('checkout/order');
    $this->load->model('payment/incasso');

    $rekNum = $this->session->data['incasso_bank_account'];

    $comment = $this->language->get('text_instruction') . "\n\n";
    $comment .= $this->config->get(
        'incasso_bank_' . $this->config->get('config_language_id')
    ) . "\n\n";
    $comment .= $this->language->get('text_payment');

    $this->model_payment_incasso->insertRekNum(
        $this->session->data['order_id'],
        $rekNum
    );

    $this->model_checkout_order->confirm(
        $this->session->data['order_id'],
        $this->config->get('incasso_order_status_id'),
        $comment,
        true
    );

    unset($this->session->data['incasso_bank_account']);

    $json['redirect'] = $this->url->link('checkout/success');

    $this->response->setOutput(json_encode($json));
}

对应的 incasso.tpl 不再读取 #bankAccountNumber,也不要将账号拼接到 URL 中:

$('#button-confirm').bind('click', function() {
    $.ajax({
        type: 'post',
        url: 'index.php?route=payment/incasso/confirm',
        dataType: 'json',
        beforeSend: function() {
            $('#button-confirm').attr('disabled', true);
        },
        complete: function() {
            $('#button-confirm').attr('disabled', false);
        },
        success: function(json) {
            if (json.error) {
                alert(json.error);
                return;
            }

            if (json.redirect) {
                location = json.redirect;
            }
        }
    });
});

修复数据库写入方式

当前模型直接将 $orderNum 和 $rekNum 拼接到 SQL 中:

VALUES ('$orderNum', '$rekNum')

这会带来 SQL 注入风险。至少要将订单编号强制转换为整数,并使用 OpenCart 数据库驱动提供的 escape():

public function insertRekNum($orderNum, $rekNum) {
    $orderNum = (int)$orderNum;
    $rekNum = $this->db->escape($rekNum);

    $sql = "INSERT INTO `" . DB_PREFIX . "order_incasso`
            SET order_id = '" . $orderNum . "',
                iban = '" . $rekNum . "'";

    $this->db->query($sql);
}

如果每个订单只能保存一个 IBAN,可以为 order_id 添加唯一索引,并按实际需求使用更新操作或 ON DUPLICATE KEY UPDATE。这样可以避免用户重复点击确认按钮后生成多条记录。

还需要注意的地方

代码中的路径似乎有拼写错误:

/catelog/controller/paymemt/incasso.php

OpenCart 的标准目录通常是:

catalog/controller/payment/incasso.php

如果这只是问题描述中的笔误,可以忽略。如果实际目录也是这样,OpenCart 的默认路由将无法正常加载该控制器。

另外,如果 Automatic Incasso 指的是 SEPA Direct Debit,只收集 IBAN 通常还不够。实际业务可能还需要账户姓名、授权声明、mandate reference、授权时间和客户的明确同意。IBAN 也属于敏感个人数据,应限制后台访问并避免写入日志。是否需要加密保存,则要根据业务所在地的数据保护要求决定。

备注:内容仅供参考。