Files
2026-04-28 15:13:50 +02:00

220 lines
5.9 KiB
PHP

<?php
/**
* Class WooPay_Webhooks
*
* @package WooCommerce\Payments
*/
namespace WCPay\WooPay;
use WC_Payments_Account;
use WC_Payments_API_Client;
use WCPay\Exceptions\API_Exception;
defined( 'ABSPATH' ) || exit;
/**
* This class introduces webhooks to delivery order updates to the associated
* orders in the woopay.
*
* WooPay Webhooks are enqueued to their associated actions, delivered, and logged.
*/
class WooPay_Order_Status_Sync {
const WCPAY_WEBHOOK_WOOPAY_ORDER_STATUS_CHANGED = 'wcpay_webhook_platform_checkout_order_status_changed';
/**
* WC_Payments_Account instance to get information about the account
*
* @var WC_Payments_Account
*/
private $account;
/**
* Client for making requests to the WooCommerce Payments API
*
* @var WC_Payments_API_Client
*/
protected $payments_api_client;
/**
* Setup webhook for the WooPay Order Status Sync.
*
* @param WC_Payments_API_Client $payments_api_client - WooCommerce Payments API client.
* @param WC_Payments_Account $account - WooCommerce Payments account.
*/
public function __construct( WC_Payments_API_Client $payments_api_client, WC_Payments_Account $account ) {
$this->payments_api_client = $payments_api_client;
$this->account = $account;
add_filter( 'woocommerce_webhook_topic_hooks', [ __CLASS__, 'add_topics' ], 20, 2 );
add_filter( 'woocommerce_webhook_payload', [ __CLASS__, 'create_payload' ], 10, 4 );
add_filter( 'woocommerce_valid_webhook_resources', [ __CLASS__, 'add_resource' ], 10, 1 );
add_filter( 'woocommerce_valid_webhook_events', [ __CLASS__, 'add_event' ], 10, 1 );
add_action( 'woocommerce_order_status_changed', [ __CLASS__, 'send_webhook' ], 10, 3 );
add_action( 'admin_init', [ $this, 'maybe_create_woopay_order_webhook' ], 10 );
}
/**
* Return the webhook name.
*
* @return string
*/
private static function get_webhook_name() {
return __( 'WooPayments woopay order status sync', 'woocommerce-payments' );
}
/**
* Maybe create the WooPay webhook under certain conditions.
*/
public function maybe_create_woopay_order_webhook() {
if ( ! current_user_can( 'manage_woocommerce' ) || self::is_webhook_created() ) {
return;
}
if ( ! $this->account->is_stripe_account_valid() || $this->account->is_account_under_review() || $this->account->is_account_rejected() ) {
return;
}
$this->register_webhook();
}
/**
* Return true if webhook was already created.
*
* @return bool
*/
private static function is_webhook_created() {
return ! empty( self::get_webhook() );
}
/**
* Return array with the webhook id for the woopay order status sync.
*
* @return array
*/
public static function get_webhook() {
$data_store = \WC_Data_Store::load( 'webhook' );
$args = [
'search' => self::get_webhook_name(),
'status' => 'active',
'limit' => 1,
];
$webhooks = $data_store->search_webhooks( $args );
return $webhooks;
}
/**
* Register the webhook on WooCommerce.
*
* @return void
*/
private function register_webhook() {
$webhook = new \WC_Webhook();
$webhook->set_name( self::get_webhook_name() );
$webhook->set_user_id( get_current_user_id() );
$webhook->set_topic( 'order.status_changed' );
$webhook->set_secret( wp_generate_password( 50, false ) );
$webhook->set_delivery_url( WooPay_Utilities::get_woopay_rest_url( 'merchant-notification' ) );
$webhook->set_status( 'active' );
$webhook->save();
try {
$this->payments_api_client->update_woopay( [ 'webhook_secret' => $webhook->get_secret() ] );
} catch ( API_Exception $e ) {
$webhook->delete();
}
}
/**
* Add order webhook topic
*
* @param array $topic_hooks List of WooCommerce's standard webhook topics and hooks.
*/
public static function add_topics( $topic_hooks ) {
$topic_hooks['order.status_changed'][] = self::WCPAY_WEBHOOK_WOOPAY_ORDER_STATUS_CHANGED;
return $topic_hooks;
}
/**
* Setup payload for the webhook delivery.
*
* @param array $payload Data to be sent out by the webhook.
* @param string $resource_name Type/name of the resource.
* @param integer $resource_id ID of the resource.
* @param integer $id ID of the webhook.
*/
public static function create_payload( $payload, $resource_name, $resource_id, $id ) {
$webhook = wc_get_webhook( $id );
if ( ! $webhook ) {
return $payload;
}
if ( 0 !== strpos( $webhook->get_delivery_url(), WooPay_Utilities::get_woopay_rest_url( 'merchant-notification' ) ) ) {
// This is not a WooPay webhook, so we don't need to modify the payload.
return $payload;
}
return [
'blog_id' => \Jetpack_Options::get_option( 'id' ),
'order_id' => $resource_id,
'order_status' => $payload['status'],
];
}
/**
* Add webhook resource for order.
*
* @param array $resources List of available resources.
*/
public static function add_resource( $resources ) {
$resources[] = 'order';
return $resources;
}
/**
* Undocumented function
*
* @param array $topic_events List of available topic events.
*/
public static function add_event( $topic_events ) {
$topic_events[] = 'status_changed';
return $topic_events;
}
/**
* Trigger webhook delivery.
*
* @param int $order_id Order id.
* @param string $previous_status the old WooCommerce order status.
* @param string $next_status the new WooCommerce order status.
* @return void
*/
public static function send_webhook( $order_id, $previous_status, $next_status ) {
$order = wc_get_order( $order_id );
if ( $order->get_meta( 'is_woopay' ) ) {
do_action( self::WCPAY_WEBHOOK_WOOPAY_ORDER_STATUS_CHANGED, $order_id, $next_status );
}
}
/**
* Removes the webhook if woopay is disabled.
*
* @return void
*/
public static function remove_webhook() {
if ( self::is_webhook_created() ) {
$webhook_id = self::get_webhook()[0];
$webhook = new \WC_Webhook( $webhook_id );
$webhook->delete();
}
}
}