namespace = CF7PA_ADDONS_REST_API . 'stripe'; $this->rest_base = 'webhook'; Stripe_Webhook_State::get_monitoring_began_at(); } public function register_routes() { register_rest_route( $this->namespace, $this->rest_base, array(array( 'methods' => \WP_REST_Server::CREATABLE, 'callback' => array($this, 'handle_webhooks'), 'permission_callback' => array($this, 'verify_access'), )) ); } public function verify_access( \WP_REST_Request $request ) { return true; } public function handle_webhooks( \WP_REST_Request $request ) { try { // Parse the message body (and check the signature if possible) $webhookSecret = Stripe_Settings::get_webhook_secret(); if ( !empty( $webhookSecret ) ) { try { $event = \Stripe\Webhook::constructEvent( $request->get_body(), $request->get_header( 'stripe-signature' ), $webhookSecret ); } catch ( \Exception $e ) { Logger::error( 'Exception on StripeWebhook crypto:' . $e->getMessage() ); Stripe_Webhook_State::set_last_webhook_failure_at( time() ); Stripe_Webhook_State::set_last_error_reason( Stripe_Webhook_State::VALIDATION_FAILED_SIGNATURE_INVALID ); return http_response_code( 403 ); } } else { $event = $request->get_json_params(); } $event = apply_filters( 'cf7pa_pay_addons/webhook/event', $event ); $type = $event['type']; $object = $event['data']['object']; if ( !$this->is_event_from_current_site( $object ) ) { $type = 'unknown'; Logger::error( 'unknown site event id:' . $object['id'] ); } switch ( $type ) { case 'checkout.session.completed': case 'checkout.session.async_payment_succeeded': case 'checkout.session.async_payment_failed': if ( $type == 'checkout.session.completed' ) { // Update transaction to success Logger::info( 'Webhook received! Checkout session event ' . $object['id'] . ' completed' ); $txn_query = new \CF7PA_Pay_Addons\Database\Transaction_Query(); $txn_query->update_by_transaction_id( $object['id'], [ 'payment_status' => 'success', 'customer_name' => $object['customer_details']['name'] ?? '', 'customer_email' => $object['customer_details']['email'] ?? '', 'payment_method' => $object['payment_method_types'][0] ?? '', ] ); // if amount is 0, it is a free checkout, also send the success email if ( $object['amount_total'] == 0 ) { } } $this->trigger_contact_form_7_submit( $object['id'], [ 'payment_intent_id' => $object['payment_intent'], 'session_checkout_id' => $object['id'], ] ); // do nothing break; case 'charge.succeeded': break; case 'invoice.upcoming': break; case 'invoice.payment_succeeded': break; case 'invoice.payment_failed': break; case 'payment_intent.payment_failed': $intent = $object; $error_message = ( $intent->last_payment_error ? $intent->last_payment_error->message : '' ); Logger::info( 'Webhook received! PaymentIntent event ' . $intent['id'] . ' failed:' . $error_message ); // Update transaction to failed $txn_query_fail = new \CF7PA_Pay_Addons\Database\Transaction_Query(); $txn_query_fail->update_by_transaction_id( $intent['id'], [ 'payment_status' => 'failed', 'comment' => $error_message, ] ); break; case 'payment_intent.succeeded': $intent = $object; // Process valid response. Logger::info( 'Webhook received! PaymentIntent event ' . $intent['id'] . ' succeeded' ); // Re-fetch PaymentIntent with payment_method expanded (not available in webhook by default) $pi = \CF7PA_Pay_Addons\Stripe\Stripe_API::retrieve_payment_intent( $intent['id'], [ 'expand' => ['payment_method'], ] ); $update_data = [ 'payment_status' => 'success', 'meta_data' => $pi->metadata ?? null, ]; if ( !empty( $pi->payment_method->type ) ) { $update_data['payment_method'] = $pi->payment_method->type; } if ( !empty( $pi->payment_method->billing_details->name ) ) { $update_data['customer_name'] = $pi->payment_method->billing_details->name; } if ( !empty( $pi->payment_method->billing_details->email ) ) { $update_data['customer_email'] = $pi->payment_method->billing_details->email; } $txn_query_ok = new \CF7PA_Pay_Addons\Database\Transaction_Query(); $txn_query_ok->update_by_transaction_id( $intent['id'], $update_data ); break; } Stripe_Webhook_State::set_last_webhook_success_at( $event['created'] ); http_response_code( 200 ); } catch ( \Exception $e ) { Logger::error( 'Exception detail:', wp_json_encode( $e ) ); return new \WP_Error('stripe_error', __( $e->getMessage() ), array( 'status' => 400, )); } } public function is_redirect_based_payment_method( $charge ) { $payment_method = $charge['payment_method_details']['type']; $redirection_prone = [ 'paypal', 'ideal', 'giropay', 'bancontact', 'sofort', 'p24', 'eps', 'fpx', 'bacs_debit', 'au_becs_debit', 'grabpay', 'oxxo', 'alipay', 'wechat_pay' ]; return in_array( $payment_method, $redirection_prone ); } public function trigger_contact_form_7_submit( $key, $extra_data = [] ) { $form_data = Form_Redirection_Submission::get_post_data_by_transient( $key ); if ( $form_data ) { // Remove the transient to prevent multiple submissions delete_transient( $key ); // Restore POST data // Set up the original AJAX action $_POST = $form_data; if ( isset( $extra_data['payment_intent_id'] ) && $extra_data['payment_intent_id'] ) { $_POST['_cf7pa_payment_intent_id'] = $extra_data['payment_intent_id']; } if ( isset( $extra_data['session_checkout_id'] ) && $extra_data['session_checkout_id'] ) { $_POST['_cf7pa_session_checkout_id'] = $extra_data['session_checkout_id']; } $form_id = $form_data['_wpcf7']; // wpcf7_skip_spam_check $skip_spam_filter = function ( $skip, $submission ) { return true; }; // Add the filter add_filter( 'wpcf7_skip_spam_check', $skip_spam_filter, 10, 2 ); // Trigger Contact Form 7 submission $contact_form = wpcf7_contact_form( $form_id ); if ( $contact_form ) { // Submit the form $result = $contact_form->submit(); if ( $result['status'] === 'mail_sent' ) { Logger::info( 'Form submitted successfully for form ID: ' . $form_id ); return true; } else { Logger::error( 'Form submission failed for form ID: ' . $form_id . '. Status: ' . $result['status'] ); return false; } } else { Logger::error( 'Contact form not found for ID: ' . $form_id ); } // Remove the filter using the stored reference remove_filter( 'wpcf7_skip_spam_check', $skip_spam_filter, 10 ); // Prevent further execution exit; } else { Logger::error( 'No transient data found for key: ' . $key ); return false; } } function is_event_from_current_site( $object ) { if ( isset( $object['metadata'] ) ) { if ( isset( $object['metadata']['referer'] ) && $object['metadata']['referer'] !== $this->get_site_domain() ) { return false; } return true; } return true; } protected function get_site_domain() { return wp_parse_url( get_site_url(), PHP_URL_HOST ); } }