支払いの流れに関して
決済において、決済の処理はそれぞれの決済の process_payment 関数によって処理されます。
process_payment が処理される場所
通常の決済では、支払いのページで「決済する」などのボタンを押すことによって決済処理がされますが、それまでにどのような処理をWooCommerce内でされているかを解説します。
まず、通常のWEBサイト上での支払いに関して、 process_payment 関数が実行される関数を見てみます。
/**
* Process an order that does require payment.
*
* @since 3.0.0
* @param int $order_id Order ID.
* @param string $payment_method Payment method.
*/
protected function process_order_payment( $order_id, $payment_method ) {
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
if ( ! isset( $available_gateways[ $payment_method ] ) ) {
return;
}
// Store Order ID in session so it can be re-used after payment failure.
WC()->session->set( 'order_awaiting_payment', $order_id );
// Process Payment.
$result = $available_gateways[ $payment_method ]->process_payment( $order_id );
// Redirect to success/confirmation/payment page.
if ( isset( $result['result'] ) && 'success' === $result['result'] ) {
$result['order_id'] = $order_id;
$result = apply_filters( 'woocommerce_payment_successful_result', $result, $order_id );
if ( ! wp_doing_ajax() ) {
// phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
wp_redirect( $result['redirect'] );
exit;
}
wp_send_json( $result );
}
}
[Process Payment.] の記述のある場所で process_payment 関数が実行されているのがわかります。それまでの間に、注文IDの注文がその決済を利用できるかどうかなどの判定を行っています。
そして、問題なければ、process_payment で出力した $result['redirect'] にリダイレクト処理をされているのが分かります。
上記のコードを読み取ると分かりますが、woocommerce_payment_successful_result のフィルターによってリダイレクト先の変更などが出来るようになっています。
process_checkout の処理
決済処理のメイン的な関数である、process_checkout 関数が処理される時に、決済される前の挙動が確認できます。
具体的には、以下です。
/**
* Process the checkout after the confirm order button is pressed.
*
* @throws Exception When validation fails.
*/
public function process_checkout() {
try {
$nonce_value = wc_get_var( $_REQUEST['woocommerce-process-checkout-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // phpcs:ignore
$expiry_message = sprintf(
/* translators: %s: shop cart url */
__( 'Sorry, your session has expired. <a href="%s" class="wc-backward">Return to shop</a>', 'woocommerce' ),
esc_url( wc_get_page_permalink( 'shop' ) )
);
if ( empty( $nonce_value ) || ! wp_verify_nonce( $nonce_value, 'woocommerce-process_checkout' ) ) {
// If the cart is empty, the nonce check failed because of session expiry.
if ( WC()->cart->is_empty() ) {
throw new Exception( $expiry_message );
}
WC()->session->set( 'refresh_totals', true );
throw new Exception( __( 'We were unable to process your order, please try again.', 'woocommerce' ) );
}
wc_maybe_define_constant( 'WOOCOMMERCE_CHECKOUT', true );
wc_set_time_limit( 0 );
do_action( 'woocommerce_before_checkout_process' );
if ( WC()->cart->is_empty() ) {
throw new Exception( $expiry_message );
}
do_action( 'woocommerce_checkout_process' );
$errors = new WP_Error();
$posted_data = $this->get_posted_data();
// Update session for customer and totals.
$this->update_session( $posted_data );
// Validate posted data and cart items before proceeding.
$this->validate_checkout( $posted_data, $errors );
foreach ( $errors->errors as $code => $messages ) {
$data = $errors->get_error_data( $code );
foreach ( $messages as $message ) {
wc_add_notice( $message, 'error', $data );
}
}
if ( empty( $posted_data['woocommerce_checkout_update_totals'] ) && 0 === wc_notice_count( 'error' ) ) {
$this->process_customer( $posted_data );
$order_id = $this->create_order( $posted_data );
$order = wc_get_order( $order_id );
if ( is_wp_error( $order_id ) ) {
throw new Exception( $order_id->get_error_message() );
}
if ( ! $order ) {
throw new Exception( __( 'Unable to create order.', 'woocommerce' ) );
}
do_action( 'woocommerce_checkout_order_processed', $order_id, $posted_data, $order );
/**
* Note that woocommerce_cart_needs_payment is only used in
* WC_Checkout::process_checkout() to keep backwards compatibility.
* Use woocommerce_order_needs_payment instead.
*
* Note that at this point you can't rely on the Cart Object anymore,
* since it could be empty see:
* https://github.com/woocommerce/woocommerce/issues/24631
*/
if ( apply_filters( 'woocommerce_cart_needs_payment', $order->needs_payment(), WC()->cart ) ) {
$this->process_order_payment( $order_id, $posted_data['payment_method'] );
} else {
$this->process_order_without_payment( $order_id );
}
}
} catch ( Exception $e ) {
wc_add_notice( $e->getMessage(), 'error' );
}
$this->send_ajax_failure_response();
}
各種注文内容の基本チェック
最初の方で、nonceチェックなどセキュリティのチェックやカートが空じゃないかセッションの時間切れかどうかなどの確認をしています。
2つのアクションフック
そして、 ‘woocommerce_before_checkout_process’ と ‘woocommerce_checkout_process’ のアクションフックがあります。ただ、このフックには変数を受け取れていないので、実際の注文データに関して、何か処理をするのではなく、何か注文内容以外での条件を行う際に処理をする形になるかと思います。
また、注文データのバリデーションの処理(必須項目の有無など)などもここで行われます。エラーのある場合は決済処理まで行かずにここで弾かれる仕組みです。
実際の決済処理への移行
‘woocommerce_checkout_update_totals’ のポストデータがある場合は、変数名の通り合計金額の更新の時に使われるので、実際には決済には移行しないように処理されています。
注文の作成および呼び出し
決済をすべき状態になったら、注文データを作り、WooCommerce の注文クラスである、WC_Order として注文データを呼び出します。create_order 関数を見れば分かりますが、最初に’woocommerce_create_order’フィルターフックが設定されており、カートのセッションにorder_idが保存されていたりすると注文を作ることはせずに、$order_idをすぐに返す形になっています。create_order 関数の詳細は別途説明します。
決済直前の注文データ向けのアクションフック
‘woocommerce_checkout_order_processed’ のアクションフックを使って、注文データの処理を行えます。
変数は $order_id, $posted_data, $order の3つですので、様々な処理を行うことができます。
決済処理
WC_Order の needs_payment 関数で決済処理が必要かどうかを判断して、処理を行います。
決済が必要な場合は、先に説明した process_order_payment 関数の処理が行われます。
process_checkout 関数が実行される時
process_checkout が実行されるのは ajax にて支払い処理をされる時と form-handler の checkout_action 関数が実行される時のみです。