WebhooksService.php
226 lines
| 1 | <?php |
| 2 | |
| 3 | namespace SureCart\Webhooks; |
| 4 | |
| 5 | use SureCart\Models\ApiToken; |
| 6 | use SureCart\Models\IncomingWebhook; |
| 7 | use SureCart\Support\Encryption; |
| 8 | use SureCart\Support\Server; |
| 9 | use SureCart\Support\URL; |
| 10 | |
| 11 | /** |
| 12 | * Webhooks service. |
| 13 | */ |
| 14 | class WebhooksService { |
| 15 | /** |
| 16 | * The registered webhook. |
| 17 | * |
| 18 | * @var \SureCart\Models\RegisteredWebhook |
| 19 | */ |
| 20 | protected $webhook; |
| 21 | |
| 22 | /** |
| 23 | * Get the registered webhook. |
| 24 | * |
| 25 | * @param \SureCart\Models\RegisteredWebhook $webhook The registered webhook. |
| 26 | */ |
| 27 | public function __construct( \SureCart\Models\RegisteredWebhook $webhook ) { |
| 28 | $this->webhook = $webhook; |
| 29 | } |
| 30 | |
| 31 | /** |
| 32 | * Bootstrap the integration. |
| 33 | * |
| 34 | * @return void |
| 35 | */ |
| 36 | public function bootstrap() { |
| 37 | // delete any old webhook processes. |
| 38 | add_action( 'delete_expired_transients', [ $this, 'deleteOldWebhookProcesses' ] ); |
| 39 | // we can skip this for localhost or non-secure connections. |
| 40 | if ( apply_filters( 'surecart/webhooks/localhost/register', $this->isLocalHost() ) || ! is_ssl() ) { |
| 41 | return; |
| 42 | } |
| 43 | // maybe create webhooks if they are not yet created. |
| 44 | \add_action( 'admin_init', [ $this, 'maybeCreate' ] ); |
| 45 | // listen for any domain changes and show notice. |
| 46 | \add_action( 'admin_notices', [ $this, 'maybeShowDomainChangeNotice' ] ); |
| 47 | // verify existing webhooks are functioning properly. |
| 48 | // \add_action( 'admin_init', [ $this, 'verify' ] ); |
| 49 | } |
| 50 | |
| 51 | /** |
| 52 | * Delete any webhook processes older than 30 days. |
| 53 | * |
| 54 | * @return void |
| 55 | */ |
| 56 | public function deleteOldWebhookProcesses() { |
| 57 | IncomingWebhook::deleteExpired( apply_filters( 'surecart/webhook/processes/log_expiration', '30 days' ) ); |
| 58 | } |
| 59 | |
| 60 | /** |
| 61 | * Maybe show a notice to the user that the domain has changed. |
| 62 | * |
| 63 | * This will prompt them to take action to either update the webhook or create a new webhook. |
| 64 | * |
| 65 | * @return string|null |
| 66 | */ |
| 67 | public function maybeShowDomainChangeNotice() { |
| 68 | $webhook = $this->webhook->get(); |
| 69 | |
| 70 | // let's handle the error elsewhere. |
| 71 | if ( is_wp_error( $webhook ) || empty( $webhook['id'] ) || empty( $webhook['url'] ) ) { |
| 72 | return; |
| 73 | } |
| 74 | |
| 75 | // the domain matches, so everything is good. |
| 76 | if ( $this->webhook->currentDomainMatches() ) { |
| 77 | return; |
| 78 | } |
| 79 | |
| 80 | // if domain does not match, then show notice. |
| 81 | wp_enqueue_style( 'surecart-webhook-admin-notices' ); |
| 82 | return \SureCart::render( |
| 83 | 'admin/notices/webhook-change', |
| 84 | [ |
| 85 | 'previous_webhook' => $webhook, |
| 86 | 'update_url' => esc_url( \SureCart::getUrl()->editModel( 'update_webhook', $webhook['id'] ) ), |
| 87 | 'add_url' => esc_url( \SureCart::getUrl()->editModel( 'create_webhook', '0' ) ), |
| 88 | 'previous_web_url' => esc_url_raw( URL::getSchemeAndHttpHost( $webhook['url'] ) ), |
| 89 | 'current_web_url' => esc_url_raw( URL::getSchemeAndHttpHost( $this->webhook->getListenerUrl() ) ), |
| 90 | ] |
| 91 | ); |
| 92 | } |
| 93 | |
| 94 | /** |
| 95 | * Do we have a token. |
| 96 | * |
| 97 | * @return boolean |
| 98 | */ |
| 99 | public function hasToken(): bool { |
| 100 | return ! empty( ApiToken::get() ); |
| 101 | } |
| 102 | |
| 103 | /** |
| 104 | * May be Create webhooks for this site. |
| 105 | * |
| 106 | * @return void |
| 107 | */ |
| 108 | public function maybeCreate(): void { |
| 109 | // Check for API key and early return if not. |
| 110 | if ( ! $this->hasToken() ) { |
| 111 | return; |
| 112 | } |
| 113 | |
| 114 | // get the saved webhook. |
| 115 | $registered = $this->webhook->get(); |
| 116 | |
| 117 | // We have one registered already. |
| 118 | if ( ! empty( $registered->id ) ) { |
| 119 | return; |
| 120 | } |
| 121 | |
| 122 | // register the webhooks. |
| 123 | $registered = $this->webhook->create(); |
| 124 | |
| 125 | // handle error and show notice to user. |
| 126 | if ( is_wp_error( $registered ) ) { |
| 127 | \SureCart::notices()->add( |
| 128 | [ |
| 129 | 'name' => 'webhooks_registration_error', |
| 130 | 'type' => 'warning', |
| 131 | 'title' => esc_html__( 'SureCart Webhook Registration Error', 'surecart' ), |
| 132 | 'text' => sprintf( '<p>%s</p>', ( implode( '<br />', $registered->get_error_messages() ?? [] ) ) ), |
| 133 | ] |
| 134 | ); |
| 135 | return; |
| 136 | } |
| 137 | |
| 138 | // send a test. |
| 139 | $registered->test(); |
| 140 | } |
| 141 | |
| 142 | /** |
| 143 | * Is this localhost? |
| 144 | * |
| 145 | * @return boolean |
| 146 | */ |
| 147 | public function isLocalHost() { |
| 148 | return ( new Server( $this->webhook->getListenerUrl() ) )->isLocalHost(); |
| 149 | } |
| 150 | |
| 151 | /** |
| 152 | * Verify webhooks. |
| 153 | * |
| 154 | * @return function |
| 155 | */ |
| 156 | // public function verify() { |
| 157 | // $webhook = $this->webhook->get(); |
| 158 | |
| 159 | // if ( is_wp_error( $webhook ) ) { |
| 160 | // not found, let's recreate one. |
| 161 | // if ( 'webhook_endpoint.not_found' === $webhook->get_error_code() ) { |
| 162 | // delete saved. |
| 163 | // $this->webhook->registration()->delete(); |
| 164 | // create. |
| 165 | // return $this->maybeCreate(); |
| 166 | // } |
| 167 | |
| 168 | // handle other errors. |
| 169 | // return \SureCart::notices()->add( |
| 170 | // [ |
| 171 | // 'name' => 'webhooks_general_error', |
| 172 | // 'type' => 'error', |
| 173 | // 'title' => esc_html__( 'SureCart Webhooks Error', 'surecart' ), |
| 174 | // 'text' => sprintf( '<p>%s</p>', ( implode( '<br />', $webhook->get_error_messages() ?? [] ) ) ), |
| 175 | // ] |
| 176 | // ); |
| 177 | // } |
| 178 | |
| 179 | // If webhook is not created, show notice. |
| 180 | // This should not happen, but just in case. |
| 181 | // if ( ! $webhook || empty( $webhook->id ) ) { |
| 182 | // return \SureCart::notices()->add( |
| 183 | // [ |
| 184 | // 'name' => 'webhooks_not_created', |
| 185 | // 'type' => 'error', |
| 186 | // 'title' => esc_html__( 'SureCart Webhooks Error', 'surecart' ), |
| 187 | // 'text' => '<p>' . esc_html__( 'Webhooks cannot be created.', 'surecart' ) . '</p>', |
| 188 | // ] |
| 189 | // ); |
| 190 | // } |
| 191 | |
| 192 | // Show the grace period notice. |
| 193 | // if ( ! empty( $webhook->erroring_grace_period_ends_at ) ) { |
| 194 | // $message = []; |
| 195 | // $message[] = $webhook->erroring_grace_period_ends_at > time() ? esc_html__( 'Your SureCart webhook connection is being monitored due to errors. This can cause issues with any of your SureCart integrations.', 'surecart' ) : esc_html__( 'Your SureCart webhook connection was disabled due to repeated errors. This can cause issues with any of your SureCart integrations.', 'surecart' ); |
| 196 | // $message[] = $webhook->erroring_grace_period_ends_at > time() ? sprintf( wp_kses( 'These errors will automatically attempt to be retried, however, we will disable this in <strong>%s</strong> if it continues to fail.', 'surecart' ), human_time_diff( $webhook->erroring_grace_period_ends_at ) ) : sprintf( wp_kses( 'It was automatically disabled %s ago.', 'surecart' ), human_time_diff( $webhook->erroring_grace_period_ends_at ) ); |
| 197 | // $message[] = __( 'If you have already fixed this you can dismiss this notice.', 'surecart' ); |
| 198 | // $message[] = '<p> |
| 199 | // <a href="' . esc_url( \SureCart::getUrl()->editModel( 'resync_webhook', $webhook['id'] ) ) . '" class="button">' . esc_html__( 'Resync Webhook', 'surecart' ) . '</a> |
| 200 | // <a href="' . esc_url( untrailingslashit( SURECART_APP_URL ) . '/developer' ) . '" target="_blank">' . esc_html__( 'Troubleshoot Connection', 'surecart' ) . '</a> |
| 201 | // </p>'; |
| 202 | |
| 203 | // return \SureCart::notices()->add( |
| 204 | // [ |
| 205 | // 'name' => 'webhooks_erroring_grace_period_' . $webhook->erroring_grace_period_ends_at, |
| 206 | // 'type' => 'warning', |
| 207 | // 'title' => esc_html__( 'SureCart Webhook Connection', 'surecart' ), |
| 208 | // 'text' => sprintf( '<p>%s</p>', ( implode( '<br />', $message ) ) ), |
| 209 | // ] |
| 210 | // ); |
| 211 | // } |
| 212 | // } |
| 213 | |
| 214 | /** |
| 215 | * Get the signing secret stored as encrypted data in the WP database. |
| 216 | * |
| 217 | * @return string|bool Decrypted value, or false on failure. |
| 218 | */ |
| 219 | public function getSigningSecret() { |
| 220 | // Get the registered webhook. |
| 221 | $webhook = $this->webhook->get(); |
| 222 | // Return the signing secret from the registered webhook. |
| 223 | return Encryption::decrypt( $webhook['signing_secret'] ?? '' ); |
| 224 | } |
| 225 | } |
| 226 |