WooCommerce 如何扩展支付方式

优质
小牛编辑
128浏览
2023-12-01

支付方式是所有电商程序的重头戏,卖家最关心的是怎么让客户的钱进到自己的账户。WooCommerce对支付方式的扩展有详细的介绍,对电商一窍不通的我决定先从文档开始学习。要知道如何在WooCommerce插件中增加支付接口,就要研究它的Payment Gateway API。

Payment Gateway API官方文档

支付网关分类

支付网关通常用一下的几种形式呈现:

基于表单—— 用户必须点击表单中的按钮,表单会提交到支付接口提供商的网站进行处理,例如PayPal standard

基于iframe——网关支付系统直接在网站的iframe中显示,例如SagePay Form

直接显示(Direct)——支付网关处理付款所需要的信息(字段)直接显示在网站的结账页面(checkout page),用户点击“place order”按钮后付款成功,例如PayPal Pro

离线付款(offline)——付款过程无需网络,例如:支票,银行转账

直接在自己网站进行支付(Direct)需要配置服务器提供安全保证(启用SSL认证等),可能还需要通过PCI认证(Payment Card Industry,通常指PCI DDS:Payment Card Industry Data Security Standard,支付卡产业数据安全标准)。如果想避免这些麻烦,使用支付网关来帮助你完成付款处理。

创建一个基本的支付网关

新的支付方式应该以WordPress插件的形式添加到WooCommerce中。创建支付网关插件之前,先来看一个简单的例子,这个例子已经集成在WooCommerce核心文件中。

解析支票支付网关

支票支付网关并没做什么复杂的事,也不与第三方产生数据交换——但它可以向你展示如何定义和加载自定义设置选项,是一个好的开始。

首先,新的网关应该是继承WC_Payment_Gateway的class,WC_Payment_Gateway是基础,负责产生设置选项、定义相关功能。

class WC_Cheque extends WC_Payment_Gateway {

接着定义构造函数(constructor)

public function __construct() {
    $this->id = 'cheque';
    $this->icon = apply_filters('woocommerce_cheque_icon', '');
    $this->has_fields = false;
 
    // Load the form fields.
    $this->init_form_fields();
 
    // Load the settings.
    $this->init_settings();
 
    // Define user set variables
    $this->title = $this->settings['title'];
    $this->description = $this->settings['description'];
 
    // Actions
    add_action('woocommerce_update_options_payment_gateways', array(&$this, 'process_admin_options'));
    add_action('woocommerce_thankyou_cheque', array(&$this, 'thankyou_page'));
 
    // Customer Emails
    add_action('woocommerce_email_before_order_table', array(&$this, 'email_instructions'), 10, 2);
}

id 是网关的唯一标识,是WooCommerce区分不同支付方式的重要依据

icon 表示网关的icon图标,应该是icon的图片url,会在结账页面显示

has_fields 如果设置为true,网关支付需要的字段会直接显示在结账页面,如果你使用前面提到的“Direct”方式,这项要设置为true

init_form_fields 会在下文解释

title 支付网关的名称,会显示在结账页面,在本例中,网关名称从后台设置中读取

description 支付网关的描述,显示在结账页面

Actions 向三个actions增加了功能:

woocommerce_update_options_payment_gateways —— 保存后台设置时执行,插入的函数负责保存我们的自定义设置

woocommerce_thankyou_cheque —— 付款结束后的thankyou页面执行该action

woocommerce_email_before_order_table —— 产生email模板时执行,可以用来加入自定义字段

接下来描述init_form_fields的功能,该方法定义我们要在后台显示的选项字段

$this->form_fields = array(
    'enabled' => array(
        'title' => __( 'Enable/Disable', 'woothemes' ),
        'type' => 'checkbox',
        'label' => __( 'Enable Cheque Payment', 'woothemes' ),
        'default' => 'yes'
    ),
    'title' => array(
        'title' => __( 'Title', 'woothemes' ),
        'type' => 'text',
        'description' => __( 'This controls the title which the user sees during checkout.', 'woothemes' ),
        'default' => __( 'Cheque Payment', 'woothemes' )
    ),
    'description' => array(
        'title' => __( 'Customer Message', 'woothemes' ),
        'type' => 'textarea',
        'description' => __( 'Let the customer know the payee and where they should be
 sending the cheque too and that their order won\'t be shipping until you receive it.', 'woothemes' ),
        'default' => 'Please send your cheque to Store Name, Store Street, Store Town,
 Store State / County, Store Postcode.'
    )
);

定义了三个选项分别用于设置:是否启用该网关、网关的名称和给用户的消息。

每个选项以下面的格式定义

'setting-name' => array(
    'title' => __( 'Title for setting', 'woothemes' ),
    'type' => 'checkbox|text|textarea',
    'label' => __( 'Label for checkbox setting', 'woothemes' ),
    'description' => __( 'Description for setting' ),
    'default' => 'default value'
),

产生的是表单元素,是产生input还是checkbox或者textarea等,是由type参数决定的

定义完选项,就要知道如何显示,于是有了下一个function

public function admin_options() {
    ?>
    <h3><?php _e('Cheque Payment', 'woothemes'); ?></h3>
    <p><?php _e('Allows cheque payments. Why would you take cheques in this day and 
age? Well you probably wouldn\'t but it does allow you to make test purchases for testing 
order emails and the \'success\' pages etc.', 'woothemes'); ?></p>
    <table class="form-table">
    <?php
        // Generate the HTML For the settings form.
        $this->generate_settings_html();
    ?>
    </table>
    <?php
} // End admin_options()

接下来要处理用户在结账页面看到的内容

function payment_fields() {
    if ($this->description) echo wpautop(wptexturize($this->description));
}
 
function thankyou_page() {
    if ($this->description) echo wpautop(wptexturize($this->description));
}

payment_fields负责显示结账页面所需的支付信息,例如选择direct方式时,可能需要用户输入信用卡卡号等。支票方式无需额外信息,所以这里只显示描述字段。

thankyou_page,该函数在thankyou页面执行,可以用来输出一些自定义信息

接下来是最重要的功能——接受付款和处理订单。支票支付网关不会产生在线支付行为,但需要将订单状态改成on-hold(告知管理员该订单正在等待支票付款),并告诉WooCommerce将用户定向到thankyou页面,这是通过返回success状态(数组形式返回)完成的

function process_payment( $order_id ) {
    global $woocommerce;
 
    $order = &new WC_Order( $order_id );
 
    // Mark as on-hold (we're awaiting the cheque)
    $order->update_status('on-hold', __('Awaiting cheque payment', 'woothemes'));
 
    // Remove cart
    $woocommerce->cart->empty_cart();
 
    // Empty awaiting payment session
    unset($_SESSION['order_awaiting_payment']);
 
    // Return thankyou redirect
    return array(
        'result'    => 'success',
        'redirect'  => add_query_arg('key', $order->order_key, add_query_arg('order', 
$order_id, get_permalink(get_option('woocommerce_thanks_page_id'))))
    );
 
}

最后,将该支付网关加入到WooCommerce中

function add_cheque_gateway( $methods ) {
    $methods[] = 'WC_Cheque'; return $methods;
}
add_filter('woocommerce_payment_gateways', 'add_cheque_gateway' );

支票支付网关的完整代码在classes/gateways/class-wc-cheque.php中

如何更改订单状态和添加通知

更改订单状态可以通过使用order class中的功能完成,例如

$order = new WC_Order( $order_id );
$order->update_status('on-hold', __('Awaiting cheque payment', 'woothemes'));

将订单状态更改为on-hold并通知店主该订单正在等待支票付款。

即使不更改订单状态,也可以添加通知,在调试时使用。

$order->add_order_note( __('IPN payment completed', 'woothemes') );

当支付完成后,用payment_complete()方法改变状态,而不是update_status()

	
$order->payment_complete();

这样可以保证存货量计算正确,订单状态正确更新。

如何新的支付方式做成插件

支票网关直接集成在WooCommerce的核心文件中,如果是你要增加新的支付方式,应该用插件形式加入。用插件形式,只需要将上述代码包裹在一个class中,如下所示,其它与正常插件没有区别

add_action('plugins_loaded', 'init_your_gateway', 0);
 
function init_your_gateway() {
 
    if ( ! class_exists( 'woocommerce_payment_gateway' ) ) { return; }

这样可保证你的代码只有在woocommerce安装时才会执行。

Direct 网关

如果使用Direct方式,付款将在网站的结账页面进行,而不会跳转到第三方网站。我们的代码要有所变化。

首先,has_fields设置为true

$this->has_fields = true;

这样checkout页面会输出一个包含付款表单的“payment_box”,字段由你定义

创建方法payment_fields——这个payment fields包含你的表单字段,例如要求用户输入信用卡详情的表单,非direct方式中,这些信息是在第三方网站输入的。下面是Payment pro的简化版

/**
 * Payment form on checkout page
 */
 function payment_fields() {
      global $woocommerce;
      ?>
      <?php if ($this->description) : ?><p><?php echo $this->description; ?></p><?php endif; ?>
      <fieldset>
      <p class="form-row form-row-first">
           <label for="paypal_pro_cart_number"><?php _e("Credit Card number", 'woothemes') ?> <span class="required">*</span></label>
           <input type="text" class="input-text" name="paypal_pro_card_number" />
      </p>
      <div class="clear"></div>
      <p class="form-row form-row-first">
           <label for="cc-expire-month"><?php _e("Expiration date", 'woothemes') ?> <span class="required">*</span></label>
           <select name="paypal_pro_card_expiration_month" id="cc-expire-month" class="woocommerce-select woocommerce-cc-month">
                <option value=""><?php _e('Month', 'woothemes') ?></option>
                <?php
                $months = array();
                for ($i = 1; $i <= 12; $i++) :
                     $timestamp = mktime(0, 0, 0, $i, 1);
                     $months[date('n', $timestamp)] = date('F', $timestamp);
                endfor;
                foreach ($months as $num => $name) printf('<option value="%u">%s</option>', $num, $name);
                ?>
           </select>
           <select name="paypal_pro_card_expiration_year" id="cc-expire-year" class="woocommerce-select woocommerce-cc-year">
                <option value=""><?php _e('Year', 'woothemes') ?></option>
                <?php
                for ($i = date('y'); $i <= date('y') + 15; $i++) printf('<option value="%u">20%u</option>', $i, $i);
                ?>
           </select>
      </p>
      <p class="form-row form-row-last">
           <label for="paypal_pro_card_csc"><?php _e("Card security code", 'woothemes') ?> 
<span class="required">*</span></label>
<input type="text" class="input-text" id="paypal_pro_card_csc" name="paypal_pro_card_csc" maxlength="4" style="width:4em;" />
      </p>
      <div class="clear"></div>
      </fieldset>
      <?php
 }

下一个功能validate_fields()是可选的,如果你需要对表单所填内容进行验证,就定义该方法,如果顺利通过验证就返回true,否则返回false。可以使用$woocommerce->add_error()方法添加错误信息并显示给用户。

最后,你需要创建一个新的process_payment( $order_id )方法,这个新方法需要获取表单中的数据并直接通过支付网关提供商进行支付。

如果支付失败,显示错误信息

$woocommerce->add_error(__('Payment error:', 'woothemes') . $error_message);
 return;

如果支付成功,将订单状态更改为“paid”,并返回一个数组

// Payment complete
 $order->payment_complete();
 
 // Remove cart
 $woocommerce->cart->empty_cart();
 
 // Empty awaiting payment session
 unset($_SESSION['order_awaiting_payment']);
 
 // Return thank you page redirect
 return array(
 'result' => 'success',
 'redirect' => add_query_arg('key', $order->order_key, add_query_arg('order', $order_id, get_permalink(get_option('woocommerce_thanks_page_id'))))
 );

To be continued …