';
if ( $display_tokenization ) {
$this->tokenization_script();
$this->saved_payment_methods();
}
$this->payment_form();
// Nonce verification already done in parent WooCommerce function.
if ( apply_filters( 'cpsw_sepa_display_save_payment_method_checkbox', $display_tokenization ) && ! $this->is_subscription_item_in_cart() && ! is_add_payment_method_page() && ! isset( $_GET['change_payment_method'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$this->save_payment_method_checkbox();
}
if ( 'live' !== Helper::get_payment_mode() ) {
echo '
';
/* translators: %1$1s - %6$6s: HTML Markup */
echo wp_kses_post( Helper::get_sepa_test_mode_description() );
echo '
';
}
echo '
';
/**
* Action after payment field.
*
* @since 1.4.0
*/
do_action( $this->id . '_after_payment_field_checkout' );
}
/**
* Tokenize card payment
*
* @since 1.4.0
*
* @param int $user_id id of current user placing .
* @param object $payment_method payment method object.
*
* @return object token object.
*/
public function create_payment_token_for_user( $user_id, $payment_method ) {
$token = new Token();
$token->set_last4( $payment_method->sepa_debit->last4 );
$token->set_gateway_id( $this->id );
$token->set_token( $payment_method->id );
$token->set_user_id( $user_id );
$token->save();
return $token;
}
/**
* Process woocommerce orders after payment is done
*
* @since 1.4.0
*
* @param int $order_id wooCommerce order id.
*
* @return array data to redirect after payment processing.
*/
public function process_payment( $order_id ) {
if ( $this->maybe_change_subscription_payment_method( $order_id ) ) {
return $this->process_change_subscription_payment_method( $order_id );
}
$order = wc_get_order( $order_id );
if ( 0 === absint( $order->get_total() ) ) {
return $this->process_zero_amount_order( $order );
}
if ( $this->is_using_saved_payment_method() ) {
return $this->process_payment_with_saved_payment_method( $order_id );
}
try {
$customer_id = $this->get_customer_id( $order );
$idempotency_key = $order->get_order_key() . time();
$data = [
'amount' => $this->get_formatted_amount( $order->get_total() ),
'currency' => $this->get_currency(),
'description' => $this->get_order_description( $order ),
'metadata' => $this->get_metadata( $order_id ),
'payment_method_types' => [ 'sepa_debit' ],
'customer' => $customer_id,
];
if ( ! empty( trim( $this->statement_descriptor ) ) ) {
$data['statement_descriptor_suffix'] = $this->statement_descriptor;
}
if ( $this->should_save_card( $order_id ) ) {
$data['setup_future_usage'] = 'off_session';
}
/* translators: %1$1s order id, %2$2s order total amount */
Logger::info( sprintf( __( 'Begin processing payment with SEPA for order %1$1s for the amount of %2$2s', 'checkout-plugins-stripe-woo' ), $order_id, $order->get_total() ) );
$intent_data = $this->get_payment_intent( $order_id, $idempotency_key, $data );
if ( $intent_data ) {
$response_data = [
'result' => 'success',
'redirect' => false,
'intent_secret' => $intent_data['client_secret'],
'save_card' => $this->should_save_card( $order_id ),
];
/**
* This is used to verify nonce for payment method checkout perform by block checkout.
*/
if ( isset( $_POST['payment_local_nonce'] ) && wp_verify_nonce( sanitize_text_field( $_POST['payment_local_nonce'] ), 'stripe_local_nonce' ) ) {
$response_data['verification_url'] = $this->get_verification_url( $order_id, $this->get_return_url( $order ), $this->should_save_card( $order_id ) );
}
return $response_data;
} else {
return [
'result' => 'fail',
'redirect' => '',
];
}
} catch ( Exception $e ) {
Logger::error( $e->getMessage(), true );
return new WP_Error( 'order-error', '' . $e->getMessage() . '
', [ 'status' => 200 ] );
}
}
/**
* Prepare payment method object
*
* @since 1.4.5
*
* @param object $payment_method payment method object from intent.
* @param object $token token object used for payment.
*
* @return object
*/
public function prepare_payment_method_zero_amount( $payment_method, $token ) {
return (object) apply_filters(
'cpsw_prepare_payment_method_args',
[
'token_id' => $token,
'customer' => $payment_method->customer,
'source' => $payment_method->id,
'source_object' => $payment_method,
'payment_method' => $payment_method->id,
'payment_method_object' => $payment_method,
]
);
}
/**
* Process zero amount order
*
* @since 1.4.5
*
* @param WC_Order $order WooCommerce order.
*/
public function process_zero_amount_order( $order ) {
$token = '';
$prepared_payment_method = '';
// Nonce verification already done in parent woocommerce function.
if ( isset( $_POST['payment_method_created'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Missing
$method_created = ! empty( $_POST['payment_method_created'] ) ? wc_clean( wp_unslash( $_POST['payment_method_created'] ) ) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Missing
$stripe_api = new Stripe_Api();
$response = $stripe_api->payment_methods( 'retrieve', [ $method_created ] );
$payment_method = $response['success'] ? $response['data'] : false;
$user = $order->get_id() ? $order->get_user() : wp_get_current_user();
$user_id = $user->ID;
$token = $this->create_payment_token_for_user( $user_id, $payment_method );
$prepared_payment_method = $this->prepare_payment_method( $payment_method, $token );
} elseif ( $this->is_using_saved_payment_method() ) {
// Nonce verification already done in parent woocommerce function.
$token = $this->get_token_from_request( $_POST ); //phpcs:ignore WordPress.Security.NonceVerification.Missing
$stripe_api = new Stripe_Api();
$response = $stripe_api->payment_methods( 'retrieve', [ $token ] );
$payment_method = $response['success'] ? $response['data'] : false;
$prepared_payment_method = $this->prepare_payment_method_zero_amount( $payment_method, $token );
}
if ( ! empty( $token ) ) {
$order->payment_complete();
$this->save_payment_method_to_order( $order, $prepared_payment_method );
return $this->process_change_subscription_payment_method( $order->get_id() );
}
return false;
}
/**
* Verify intent state and redirect.
*
* @since 1.4.0
*
* @return void
*/
public function verify_intent() {
$checkout_url = wc_get_checkout_url();
$order_id = isset( $_GET['order'] ) ? sanitize_text_field( $_GET['order'] ) : 0; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
// Check for empty order id.
if ( empty( $order_id ) ) {
wc_add_notice( __( 'No orders are found for provided order ID.', 'checkout-plugins-stripe-woo' ), 'error' );
Logger::error( __( 'Invalid order Id.', 'checkout-plugins-stripe-woo' ) );
wp_safe_redirect( $checkout_url );
exit();
}
$order = wc_get_order( $order_id );
// Check for empty order object.
if ( empty( $order ) ) {
wc_add_notice( __( 'No valid order found.', 'checkout-plugins-stripe-woo' ), 'error' );
Logger::error( __( 'Invalid order. No order found for provided order ID.', 'checkout-plugins-stripe-woo' ) );
wp_safe_redirect( $checkout_url );
exit();
}
if ( ! isset( $_GET['order_key'] ) || ! $order->key_is_valid( wc_clean( $_GET['order_key'] ) ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
wc_add_notice( __( 'Invalid order key received.', 'checkout-plugins-stripe-woo' ), 'error' );
Logger::error( __( 'Invalid order key.', 'checkout-plugins-stripe-woo' ) );
wp_safe_redirect( $checkout_url );
exit();
}
$intent_secret = $order->get_meta( '_cpsw_intent_secret' );
$stripe_api = new Stripe_Api();
$response = $stripe_api->payment_intents( 'retrieve', [ $intent_secret['id'] ] );
$intent = $response['success'] ? $response['data'] : false;
if ( ! $intent ) {
return;
}
if ( isset( $_GET['save_card'] ) && '1' === $_GET['save_card'] ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
$user = $order->get_id() ? $order->get_user() : wp_get_current_user();
$user_id = $user->ID;
$payment_method = $intent->payment_method;
$response = $stripe_api->payment_methods( 'retrieve', [ $payment_method ] );
$payment_method = $response['success'] ? $response['data'] : false;
$token = $this->create_payment_token_for_user( $user_id, $payment_method );
/* translators: %1$1s order id, %2$2s token id */
Logger::info( sprintf( __( 'Payment method tokenized for Order id - %1$1s with token id - %2$2s', 'checkout-plugins-stripe-woo' ), $order_id, $token->get_id() ) );
$prepared_payment_method = $this->prepare_payment_method( $payment_method, $token );
$this->save_payment_method_to_order( $order, $prepared_payment_method );
}
if ( 'succeeded' === $intent->status ) {
$redirect_to = $this->process_order( end( $intent->charges->data ), $order_id );
$redirect_url = apply_filters( 'cpsw_redirect_order_url', $redirect_to, $order );
} elseif ( 'pending' === $intent->status || 'processing' === $intent->status ) {
$order_stock_reduced = $order->get_meta( '_order_stock_reduced', true );
if ( ! $order_stock_reduced ) {
wc_reduce_stock_levels( $order_id );
}
$order->set_transaction_id( $intent->id );
$others_info = __( 'Payment will be completed once payment_intent.succeeded webhook received from Stripe.', 'checkout-plugins-stripe-woo' );
/* translators: transaction id, other info */
$order->update_status( 'on-hold', sprintf( __( 'Stripe charge awaiting payment: %1$s. %2$s', 'checkout-plugins-stripe-woo' ), $intent->id, $others_info ) );
$redirect_to = $this->get_return_url( $order );
$redirect_url = apply_filters( 'cpsw_redirect_order_url', $redirect_to, $order );
} elseif ( isset( $response['data']->last_payment_error ) ) {
$message = isset( $response['data']->last_payment_error->message ) ? $response['data']->last_payment_error->message : '';
// translators: %s: payment fail message.
wc_add_notice( sprintf( __( 'Payment failed. %s', 'checkout-plugins-stripe-woo' ), $message ), $notice_type = 'error' );
/* translators: %1$1s order id, %2$2s payment fail message. */
Logger::error( sprintf( __( 'Payment failed for Order id - %1$1s. %2$2s', 'checkout-plugins-stripe-woo' ), $order_id, $message ) );
$redirect_url = $checkout_url;
}
wp_safe_redirect( $redirect_url );
exit();
}
/**
* Modify redirect url
*
* @since 1.4.0
*
* @param array $result redirect url array.
* @param int $order_id woocommerce order id.
*
* @return array modified redirect url array.
*/
public function modify_successful_payment_result( $result, $order_id ) {
if ( empty( $order_id ) ) {
return $result;
}
$order = wc_get_order( $order_id );
if ( $this->id !== $order->get_payment_method() ) {
return $result;
}
if ( ! isset( $result['intent_secret'] ) ) {
return $result;
}
// Put the final thank you page redirect into the verification URL.
$verification_url = add_query_arg(
[
'order' => $order_id,
'confirm_payment_nonce' => wp_create_nonce( 'cpsw_confirm_payment_intent' ),
'redirect_to' => rawurlencode( $result['redirect'] ),
'save_card' => $result['save_card'],
'order_key' => $order->get_order_key(),
],
WC_AJAX::get_endpoint( $this->id . '_verify_payment_intent' )
);
// Combine into a hash.
$redirect = sprintf( '#confirm-pi-%s:%s:%s', $result['intent_secret'], rawurlencode( $verification_url ), $this->id );
return [
'result' => 'success',
'redirect' => $redirect,
];
}
}