如何为 Learnpress 添加 IDEAL payment method?
明确结论
不要修改 payment-method.php。这段代码只是结账页的展示模板,用于遍历并显示已经注册的支付网关,并不负责创建支付方式。即使直接在模板中加入 iDEAL 单选框,让它出现在页面上,也无法完成付款请求、回调验证和订单状态更新。LearnPress 升级时,这类修改还会被覆盖。
可以采用以下两种方式:
-
推荐通过 WooCommerce 接入 iDEAL。安装 LearnPress 的 WooCommerce 支付集成组件,再安装支持 iDEAL 的 WooCommerce 支付网关,例如 Mollie、Stripe 或其他荷兰支付服务商提供的插件。
-
如果业务有特殊要求,可以开发独立的 LearnPress 支付网关插件。该插件需要注册支付方式、创建支付请求、跳转到支付服务商、处理 webhook,并根据服务端查询结果更新 LearnPress 订单。
为什么修改模板没有用
问题中的代码大致执行以下逻辑:
foreach ( $payment_gateways as $gateway ) {
// 输出支付方式单选框
// 输出网关标题、描述和付款表单
}
其中 $gateway->id、$gateway->get_title() 和 $gateway->get_payment_form() 都来自已经注册的网关对象。
也就是说,payment-method.php 只负责显示支付方式,无法完成以下工作:
- 向 iDEAL 服务商创建交易;
- 生成支付跳转地址;
- 验证 webhook;
- 防止重复处理通知;
- 将 LearnPress 订单从
pending更新为completed; - 处理取消、失败和退款等状态。
iDEAL 也不是一个简单的“银行转账”选项。网站必须通过支付服务商(PSP)接入,例如 Mollie、Stripe、Adyen 或 Buckaroo。具体能够使用哪家服务商,取决于商户所在地区、账户资质以及服务商当前的产品支持情况。
推荐:通过 WooCommerce 接入
如果网站没有绕过 WooCommerce 的硬性要求,通过 WooCommerce 接入通常更容易维护。
基本流程如下:
- 安装并启用 WooCommerce。
- 安装 LearnPress 官方提供或与当前版本兼容的 WooCommerce 支付集成组件。
- 安装支付服务商提供的 WooCommerce 网关插件。
- 在 WooCommerce 支付设置中启用 iDEAL。
- 配置支付服务商提供的 API Key、Webhook 或其他商户凭据。
- 确认 LearnPress 课程结账已经交由 WooCommerce 处理。
- 分别测试付款成功、付款取消、付款失败以及 webhook 延迟到达的情况。
插件名称、配置入口和兼容范围可能会随 LearnPress、WooCommerce 及支付插件的版本变化,应以当前安装版本的说明为准。后台出现 iDEAL 选项并不代表接入已经完成。上线前还要确认付款完成后,LearnPress 订单状态和课程访问权限都能正确更新。
定制独立网关的实现结构
如果需要直接连接 LearnPress,建议创建一个普通的 WordPress 插件,例如:
learnpress-ideal-gateway/
├── learnpress-ideal-gateway.php
├── includes/
│ └── class-lp-gateway-ideal.php
└── templates/
└── payment-form.php
不要把代码放进 LearnPress 插件目录,也不要写入主题的结账模板。
LearnPress 不同版本使用的网关注册钩子、抽象类方法和订单 API 可能不同。下面的代码只用于说明实现结构。在没有核对当前版本源码之前,不应直接用于生产环境。
注册网关
<?php
/**
* Plugin Name: LearnPress iDEAL Gateway
* Description: Adds an iDEAL payment gateway to LearnPress.
* Version: 1.0.0
*/
defined( 'ABSPATH' ) || exit;
add_action( 'plugins_loaded', function () {
if ( ! class_exists( 'LP_Gateway_Abstract' ) ) {
return;
}
require_once __DIR__ . '/includes/class-lp-gateway-ideal.php';
} );
/*
* 该过滤器名称和数组格式必须根据当前 LearnPress 版本确认。
*/
add_filter( 'learn-press/payment-gateways', function ( $gateways ) {
$gateways['ideal'] = 'LP_Gateway_Ideal';
return $gateways;
} );
部分 LearnPress 版本可能采用不同的注册方式。可以查看同一版本中的现有网关实现,例如 PayPal 网关,并确认以下内容:
- 注册网关所使用的 filter;
- 注册值是类名、对象还是配置数组;
process_payment()的参数和返回值;- LearnPress 订单状态允许使用哪些值;
- 如何声明和读取设置字段。
网关类示例
<?php
defined( 'ABSPATH' ) || exit;
class LP_Gateway_Ideal extends LP_Gateway_Abstract {
public function __construct() {
$this->id = 'ideal';
$this->method_title = 'iDEAL';
$this->method_description = 'Pay securely with iDEAL.';
$this->title = 'iDEAL';
$this->description = 'You will be redirected to your bank to complete payment.';
parent::__construct();
}
public function get_payment_form() {
return '<p>' . esc_html(
'After placing the order, you will be redirected to the payment provider.'
) . '</p>';
}
public function process_payment( $order_id ) {
$order = learn_press_get_order( $order_id );
if ( ! $order ) {
return array(
'result' => 'fail',
'message' => 'Invalid order.',
);
}
/*
* 1. 从 LearnPress 订单读取金额、货币和订单号。
* 2. 使用支付服务商官方 SDK 或 REST API 创建付款。
* 3. metadata 中保存 LearnPress order ID。
* 4. 设置 return URL 和 webhook URL。
* 5. 保存服务商返回的 payment ID。
* 6. 返回或执行到 checkout URL 的跳转。
*/
$payment = $this->create_provider_payment( $order );
if ( empty( $payment['id'] ) || empty( $payment['checkout_url'] ) ) {
return array(
'result' => 'fail',
'message' => 'Unable to create payment.',
);
}
update_post_meta(
$order_id,
'_ideal_payment_id',
sanitize_text_field( $payment['id'] )
);
return array(
'result' => 'success',
'redirect' => esc_url_raw( $payment['checkout_url'] ),
);
}
private function create_provider_payment( $order ) {
/*
* 这里调用所选支付服务商的 API。
* 不要在浏览器端暴露 Secret API Key。
*/
return array();
}
}
这里的返回数组采用了常见的支付网关形式,但当前 LearnPress 版本可能要求其他字段,也可能要求网关直接执行重定向。实际开发时,必须按照已安装版本的 LP_Gateway_Abstract 和内置网关实现进行调整。
创建支付请求
创建付款时,至少需要向支付服务商发送以下信息:
$payload = array(
'amount' => array(
'currency' => $currency,
'value' => number_format( $amount, 2, '.', '' ),
),
'description' => sprintf( 'LearnPress order #%d', $order_id ),
'redirectUrl' => $return_url,
'webhookUrl' => $webhook_url,
'metadata' => array(
'learnpress_order_id' => $order_id,
),
);
这段代码只是通用示例。字段名称、金额格式、签名方式和接口地址,都必须遵循所选支付服务商的官方 API 定义。
支付创建成功后,应保存服务商返回的 payment ID,不能只依赖浏览器返回的查询参数:
update_post_meta(
$order_id,
'_ideal_payment_id',
sanitize_text_field( $provider_payment_id )
);
处理 webhook
可以通过 WordPress REST API 提供 webhook 地址:
add_action( 'rest_api_init', function () {
register_rest_route(
'learnpress-ideal/v1',
'/webhook',
array(
'methods' => 'POST',
'callback' => 'lp_ideal_handle_webhook',
'permission_callback' => '__return_true',
)
);
} );
function lp_ideal_handle_webhook( WP_REST_Request $request ) {
$payment_id = sanitize_text_field(
(string) $request->get_param( 'id' )
);
if ( $payment_id === '' ) {
return new WP_REST_Response( array( 'ok' => false ), 400 );
}
/*
* 必须使用服务器端 API Key 向支付服务商查询交易。
* 不要仅相信 webhook 请求中的 status、amount 或 order_id。
*/
$payment = lp_ideal_fetch_payment_from_provider( $payment_id );
if ( ! $payment ) {
return new WP_REST_Response( array( 'ok' => false ), 400 );
}
$order_id = absint( $payment['metadata']['learnpress_order_id'] ?? 0 );
$order = learn_press_get_order( $order_id );
if ( ! $order ) {
return new WP_REST_Response( array( 'ok' => false ), 404 );
}
/*
* 还应核对:
* - 保存的 payment ID 是否一致;
* - 实付金额是否等于订单金额;
* - 币种是否一致;
* - 商户账户是否正确;
* - 该通知是否已经处理过。
*/
if ( $payment['status'] === 'paid' ) {
/*
* 使用当前 LearnPress 版本提供的正式订单 API,
* 将订单标记为已付款/已完成,并授予课程权限。
*/
} elseif ( in_array(
$payment['status'],
array( 'failed', 'canceled', 'expired' ),
true
) ) {
/*
* 根据业务规则把订单标记为失败或取消。
*/
}
return new WP_REST_Response( array( 'ok' => true ), 200 );
}
将 permission_callback 设为允许访问,是为了让外部支付服务能够调用 webhook,但这不等于可以省略验证。真正的授权依据应当是签名校验,或者使用收到的 payment ID 向支付服务商查询交易,再核对金额、币种和订单关联信息。
返回页面不能作为付款凭证
用户付款后通常会被重定向回网站,但浏览器返回页面只能用于展示结果,不能用来确认付款。
完整流程应当是:
创建 LearnPress 待付款订单
→ 服务端向支付商创建 iDEAL 交易
→ 用户跳转到银行付款
→ 支付商发送 webhook
→ 网站从支付商服务端查询交易
→ 核对金额、币种和订单号
→ 更新 LearnPress 订单并开通课程
用户付款后可能直接关闭页面,导致 return URL 根本没有被访问。另一方面,return URL 中的参数也可能被人为伪造。因此,是否开通课程必须以经过验证的服务端支付状态为准。
上线前的注意事项
- 不要修改
payment-method.php或 LearnPress 核心文件。 - Secret API Key 只能保存在服务器端,不能输出到 HTML 或 JavaScript。
- 金额必须使用支付服务商规定的格式,避免浮点数精度问题。
- webhook 必须能够安全处理重复调用,已经完成的订单不能被重复记账或重复开通课程。
- 必须核对 payment ID、订单号、金额和币种。
- 新订单应先保持待付款状态,不能在用户跳转到银行时就标记为完成。
- 使用 HTTPS,日志中不要记录 API Key、完整请求头或敏感支付信息。
- 测试付款成功、取消、失败、过期、重复通知和延迟通知等情况。
- 退款通常需要单独实现。修改 LearnPress 订单状态并不一定会自动向用户退款。
- 更新 LearnPress 前,应先在测试环境检查网关注册、结账跳转、webhook 和课程授权流程。
如果只是要让 LearnPress 支持 iDEAL,较合适的做法是通过 WooCommerce 支付集成连接成熟的 iDEAL 网关。只有结账流程、服务商接口或订单逻辑存在特殊要求时,维护独立的 LearnPress 网关插件才有必要。
备注:内容仅供参考。
