PAYATHON 2026

如何为使用 WordPress 创建的 Soho Hotel Theme 网站集成支付网关?

支付阿杰

结论

支付网关通常不是直接“集成到 Soho Hotel Theme”中,而是接入网站正在使用的预订或结账系统。首先要确认酒店订单由哪个插件处理:

  • 如果预订最终生成 WooCommerce 订单,应安装相应的 WooCommerce 支付网关插件,并在 WooCommerce 中配置。
  • 如果使用 Soho Hotel Theme 自带或捆绑的预订插件,应优先采用该插件支持的支付方式、官方扩展或接口。
  • 如果当前预订系统不支持目标网关,则需要根据网关 API 开发 WordPress 插件,处理支付创建、回调验签和订单状态同步。

仅修改主题模板或添加付款按钮,无法构成完整且安全的支付集成。

先确认订单由谁处理

可以在 WordPress 后台检查以下内容:

  1. 打开“插件”页面,查看是否安装了 WooCommerce,以及名称中带有 Booking、Hotel、Reservation 等字样的插件。
  2. 创建一笔测试预订,检查后台是否生成 WooCommerce 订单。
  3. 查看后台有没有“支付方式”“Payment Gateways”或类似设置。
  4. 确认结账页面由哪个插件生成。

不同版本或发行渠道的 Soho Hotel Theme 可能搭配不同的预订组件,不能只根据主题名称判断配置入口。具体设置应以网站当前安装的插件及其文档为准。

方案一:通过 WooCommerce 接入

如果每笔预订都会生成 WooCommerce 订单,通过 WooCommerce 接入通常最省事,后续维护成本也比较低。

安装支付网关插件

在“插件 → 安装插件”中搜索支付服务商提供的 WooCommerce 插件,例如 Stripe、PayPal,或者当地银行和聚合支付平台提供的插件。

最好选择由支付服务商或 WooCommerce 官方维护的扩展。安装前需要确认:

  • 是否支持当前使用的 WordPress、WooCommerce 和 PHP 版本;
  • 是否支持网站的币种和经营地区;
  • 是否支持退款、预授权、押金或延迟扣款;
  • 是否提供 Sandbox/Test Mode;
  • 是否依赖 webhook;
  • 是否兼容当前的预订插件。

配置支付方式

配置入口通常在:

WordPress 后台
→ WooCommerce
→ 设置
→ 付款

启用相应网关后,一般需要填写:

  • Merchant ID;
  • API Key 或 Client ID;
  • API Secret;
  • Webhook Secret;
  • 测试环境和正式环境开关;
  • 支付成功、失败及取消后的跳转设置;
  • 支持的币种;
  • webhook/callback URL。

实际字段名称由支付服务商决定。不要把测试环境密钥用于正式环境,也不要在前端 JavaScript 或主题模板中暴露 API Secret。

检查预订产品设置

酒店房型或预订项目必须能够生成正确的订单金额,还需要逐项确认:

  • 房价、税费和服务费是否计入订单总额;
  • 订金与尾款是否按预期计算;
  • 优惠券是否正确影响应付金额;
  • 付款完成后是否锁定库存或房态;
  • 付款失败、取消或超时后是否释放房态;
  • 退款后订单状态与预订状态是否同步。

如果预订插件只负责展示房型,没有把预订转换为 WooCommerce 订单,那么只安装 WooCommerce 网关不会生效。此时还需要预订插件提供 WooCommerce 桥接功能。

方案二:使用预订插件自带的支付功能

如果 Soho Hotel Theme 配套的预订插件自行管理订单,可以进入该插件的设置页面,查找类似以下路径的选项:

Booking Settings
→ Payment
→ Payment Gateways

具体菜单名称可能有所不同。常见设置包括:

  • 启用在线支付;
  • 选择支付渠道;
  • 设置全额付款或订金比例;
  • 填写商户账号和 API 凭据;
  • 配置测试模式;
  • 设置支付成功、取消和失败页面;
  • 注册 webhook 地址;
  • 设置已付款订单对应的预订状态。

如果插件只支持少量内置网关,不要直接修改插件源码。升级插件时,这些改动会被覆盖,还可能造成订单状态无法正常同步。应先查找官方扩展,或者通过插件公开的 action、filter、REST API 完成接入。

方案三:开发自定义 WooCommerce 网关

没有现成插件时,再考虑自行开发。代码应放在独立插件中,不要写入 Soho Hotel Theme 的父主题。

下面是一个简化的跳转式 WooCommerce 网关结构。示例中的接口地址、字段和签名方式均为占位内容,必须根据实际支付服务商的 API 文档替换。

<?php
/**
 * Plugin Name: Custom Hotel Payment Gateway
 */

defined('ABSPATH') || exit;

add_action('plugins_loaded', function () {
    if (!class_exists('WC_Payment_Gateway')) {
        return;
    }

    class WC_Gateway_Hotel_Custom extends WC_Payment_Gateway
    {
        public function __construct()
        {
            $this->id                 = 'hotel_custom';
            $this->method_title       = 'Hotel Custom Gateway';
            $this->method_description = 'Redirect customers to an external payment page.';
            $this->has_fields         = false;

            $this->init_form_fields();
            $this->init_settings();

            $this->title      = $this->get_option('title');
            $this->enabled    = $this->get_option('enabled');
            $this->merchant_id = $this->get_option('merchant_id');
            $this->api_secret  = $this->get_option('api_secret');

            add_action(
                'woocommerce_update_options_payment_gateways_' . $this->id,
                [$this, 'process_admin_options']
            );
        }

        public function init_form_fields()
        {
            $this->form_fields = [
                'enabled' => [
                    'title'   => 'Enable',
                    'type'    => 'checkbox',
                    'label'   => 'Enable this payment method',
                    'default' => 'no',
                ],
                'title' => [
                    'title'   => 'Title',
                    'type'    => 'text',
                    'default' => 'Online payment',
                ],
                'merchant_id' => [
                    'title' => 'Merchant ID',
                    'type'  => 'text',
                ],
                'api_secret' => [
                    'title' => 'API Secret',
                    'type'  => 'password',
                ],
            ];
        }

        public function process_payment($order_id)
        {
            $order = wc_get_order($order_id);

            if (!$order) {
                wc_add_notice('Unable to create the payment.', 'error');
                return ['result' => 'failure'];
            }

            $response = wp_remote_post('https://api.example.com/payments', [
                'timeout' => 30,
                'headers' => [
                    'Authorization' => 'Bearer ' . $this->api_secret,
                    'Content-Type'  => 'application/json',
                ],
                'body' => wp_json_encode([
                    'merchant_id' => $this->merchant_id,
                    'reference'   => (string) $order->get_id(),
                    'amount'      => (string) $order->get_total(),
                    'currency'    => $order->get_currency(),
                    'return_url'  => $this->get_return_url($order),
                ]),
            ]);

            if (is_wp_error($response)) {
                wc_add_notice('The payment service is temporarily unavailable.', 'error');
                return ['result' => 'failure'];
            }

            $status = wp_remote_retrieve_response_code($response);
            $data   = json_decode(wp_remote_retrieve_body($response), true);

            if ($status < 200 || $status >= 300 || empty($data['checkout_url'])) {
                wc_add_notice('The payment could not be initialized.', 'error');
                return ['result' => 'failure'];
            }

            $order->update_status('pending', 'Waiting for payment confirmation.');

            return [
                'result'   => 'success',
                'redirect' => esc_url_raw($data['checkout_url']),
            ];
        }
    }
});

add_filter('woocommerce_payment_gateways', function ($gateways) {
    $gateways[] = 'WC_Gateway_Hotel_Custom';
    return $gateways;
});

生产环境还必须处理服务端 webhook。不能因为用户打开了“支付成功”页面,就直接将订单标记为已付款。

回调处理至少包括:

  1. 读取支付平台发送的原始请求体;
  2. 使用 webhook secret 验证签名;
  3. 核对商户号、金额、币种和支付状态;
  4. 根据可信的商户订单号查找订单;
  5. 防止重复处理同一事件;
  6. 支付成功后调用 $order->payment_complete();
  7. 保存支付平台的交易编号;
  8. 返回支付平台要求的 HTTP 状态码。

示意代码如下:

add_action('rest_api_init', function () {
    register_rest_route('hotel-payment/v1', '/webhook', [
        'methods'             => 'POST',
        'callback'            => 'hotel_payment_webhook',
        'permission_callback' => '__return_true',
    ]);
});

function hotel_payment_webhook(WP_REST_Request $request)
{
    $raw_body  = $request->get_body();
    $signature = $request->get_header('x-payment-signature');

    if (!hotel_verify_signature($raw_body, $signature)) {
        return new WP_REST_Response(['error' => 'Invalid signature'], 401);
    }

    $event = json_decode($raw_body, true);

    if (!is_array($event) || empty($event['order_id'])) {
        return new WP_REST_Response(['error' => 'Invalid payload'], 400);
    }

    $order = wc_get_order(absint($event['order_id']));

    if (!$order) {
        return new WP_REST_Response(['error' => 'Order not found'], 404);
    }

    if (($event['status'] ?? '') === 'paid' && !$order->is_paid()) {
        // 在此之前还应核对金额、币种、商户号和事件唯一标识。
        $transaction_id = sanitize_text_field($event['transaction_id'] ?? '');
        $order->payment_complete($transaction_id);
    }

    return new WP_REST_Response(['received' => true], 200);
}

hotel_verify_signature() 必须使用支付服务商规定的算法实现,例如 HMAC-SHA256。签名字段的拼接方式也必须以服务商文档为准,不能自行猜测。

上线前必须测试

至少应覆盖以下场景:

  • 正常付款成功;
  • 用户取消付款;
  • 支付失败;
  • 支付页面超时;
  • webhook 延迟或重复发送;
  • 用户付款后没有返回网站;
  • 金额或币种不匹配;
  • 全额退款和部分退款;
  • 同一房间被多人同时预订;
  • 移动端结账;
  • 邮件通知和订单状态变化;
  • 从测试环境切换到正式环境后的真实小额交易。

测试时还应确认 Soho Hotel Theme 的缓存、AJAX 结账和页面优化功能是否会影响支付脚本。结账页、订单确认页和 webhook 地址通常不应使用页面缓存。

安全与维护注意事项

  • 网站必须启用有效的 HTTPS。
  • 优先使用支付平台托管的结账页或令牌化组件,避免服务器直接接触银行卡信息。
  • API Secret 和 webhook secret 只能保存在服务端。
  • webhook 必须验签,并核对金额、币种和订单号。
  • 不要根据前端参数或跳转 URL 判断付款是否成功。
  • 自定义代码应放入独立插件,至少也要放在子主题中。
  • 更新 WordPress、WooCommerce、预订插件或 Soho Hotel Theme 前,先在测试站验证完整的支付流程。
  • 启用日志时,不要记录银行卡资料、验证码、完整密钥或其他敏感信息。
  • 正式收款前,还应确认支付服务商的开户、币种、税务和 PCI DSS 要求。

具体选择哪种集成方式,取决于当前 Soho Hotel 网站使用的预订插件和目标支付网关。确认这两项后,才能确定准确的后台入口、兼容插件和 API 实现方式。

备注:内容仅供参考。