Skip to main content

Technical Architecture

Understanding the code structure and class organization of Signifyd for WooCommerce.

Plugin Structure

signifyd-for-woocommerce/
├── signifyd-for-woocommerce.php # Main plugin file (bootstrap)
├── composer.json # Composer dependencies
├── README.md # Plugin changelog
├── inc/ # Core classes
│ ├── class-plugin.php # Main plugin orchestration
│ ├── class-admin.php # Admin UI and meta boxes
│ ├── class-orders.php # Order processing and submission
│ ├── class-payload.php # API payload construction
│ ├── class-settings.php # Settings page and options
│ ├── class-webhook-routes.php # REST API webhook endpoints
│ ├── class-session-finger-print.php # Device fingerprinting
│ └── class-log.php # Logging functionality
├── lib/ # Third-party libraries
│ └── signifyd-php/ # Signifyd PHP SDK (if used)
└── vendor/ # Composer dependencies

Class Diagram

Plugin (Main Orchestrator)
├── Admin (UI & Meta Boxes)
├── Orders (Order Processing)
│ └── Payload (Payload Builder)
├── Settings (Configuration)
├── Webhook_Routes (REST API)
├── Session_Finger_Print (Device ID)
└── Log (Logging System)

Core Classes

Plugin Class

File: inc/class-plugin.php
Namespace: \Suma\Signifyd\Plugin

Main orchestrator responsible for initialization and dependency loading.

Key Methods:

  • activate() — Runs on plugin activation
  • deactivate() — Runs on plugin deactivation
  • loadDependencies() — Loads all plugin classes
  • run() — Hooks into WordPress

Initialization Flow:

1. WordPress loads signifyd-for-woocommerce.php
2. Plugin constants defined (SSROOT, SSINC, VERSION)
3. Plugin class instantiated
4. Activation/deactivation hooks registered
5. run() method hooks into 'plugins_loaded'
6. loadDependencies() creates class instances

Code Example:

<?php
namespace Suma\Signifyd;

class Plugin {
public function run() {
add_action('plugins_loaded', [$this, 'loadDependencies']);
}

public function loadDependencies() {
if (class_exists('WooCommerce')) {
require_once(SSINC . 'class-log.php');
require_once(SSINC . 'class-admin.php');
// ... load other classes

new Admin();
new Orders();
new Settings();
// ... instantiate classes
}
}
}

Orders Class

File: inc/class-orders.php
Namespace: \Suma\Signifyd\Orders

Handles order data collection and submission to Signifyd API.

Responsibilities:

  • Listen for WooCommerce order status changes
  • Collect order data (billing, shipping, items, customer)
  • Build API payload
  • Submit orders to Signifyd
  • Process API responses
  • Update order meta with Signifyd data

Key Hooks:

add_action('woocommerce_payment_complete', [$this, 'submit_order']);
add_action('woocommerce_order_status_processing', [$this, 'submit_order']);

Workflow:

1. WooCommerce order reaches "processing" status
2. Orders class hooked to status change
3. Check if plugin enabled and order qualifies
4. Collect order data via helper methods
5. Build payload using Payload class
6. Submit to Signifyd API via cURL
7. Parse response and save to order meta
8. Log request/response
9. Fire action hooks for third-party integrations

Key Methods:

  • submit_order($order_id) — Main submission handler
  • get_order_data($order) — Extracts order information
  • get_customer_data($order) — Extracts customer data
  • get_line_items($order) — Formats product line items
  • send_to_signifyd($payload) — Makes API call
  • handle_response($response, $order) — Processes API response

Payload Class

File: inc/class-payload.php
Namespace: \Suma\Signifyd\Payload

Constructs the JSON payload sent to Signifyd API.

Payload Structure:

[
'purchase' => [
'orderSessionId' => '',
'browserIpAddress' => '',
'orderId' => '',
'createdAt' => '',
'totalPrice' => '',
'currency' => '',
'products' => []
],
'recipient' => [
'fullName' => '',
'confirmationEmail' => '',
'deliveryAddress' => []
],
'card' => [
'bin' => '', // First 6 digits
'last4' => '' // Last 4 digits
],
'userAccount' => [
'email' => '',
'username' => '',
'accountNumber' => '',
'createdDate' => ''
]
]

Methods:

  • build_payload($order) — Main payload builder
  • get_purchase_data($order) — Purchase section
  • get_recipient_data($order) — Shipping recipient section
  • get_card_data($order) — Payment card section
  • get_user_account_data($order) — Customer account section
  • sanitize_data($data) — Cleans data for API submission

Webhook_Routes Class

File: inc/class-webhook-routes.php
Namespace: \Suma\Signifyd\Webhook_Routes

Registers REST API endpoints for receiving Signifyd webhooks.

Endpoint:

POST /wp-json/suma-signifyd/v1/webhook

Request Flow:

1. Signifyd sends webhook POST request
2. WordPress REST API routes to endpoint
3. HMAC signature validation (security)
4. Parse webhook payload
5. Extract order ID and decision
6. Update order status based on decision
7. Fire WordPress actions
8. Log webhook data
9. Return JSON response with status

Key Methods:

  • register_routes() — Registers REST API endpoints
  • handle_webhook($request) — Main webhook handler
  • validate_signature($request) — HMAC validation
  • process_decision($order_id, $decision) — Updates order
  • log_webhook($data) — Webhook-specific logging

HMAC Validation:

$signature = hash_hmac(
'sha256',
$request->get_body(),
$webhook_secret,
false
);

if (!hash_equals($signature, $request_signature)) {
return new WP_Error('invalid_signature', 'Invalid HMAC signature', ['status' => 401]);
}

Decision Actions:

switch ($decision) {
case 'ACCEPT':
case 'APPROVED':
// Update order to processing
do_action('signifyd_order_approved', $order_id);
break;

case 'REJECT':
case 'DECLINED':
// Update order to failed
do_action('signifyd_order_rejected', $order_id);
break;

case 'HOLD':
case 'REVIEW':
// Update order to on-hold
do_action('signifyd_order_held', $order_id);
break;
}

Admin Class

File: inc/class-admin.php
Namespace: \Suma\Signifyd\Admin

Handles admin interface additions and order meta boxes.

Features:

  • Adds Signifyd meta box to order edit screen
  • Displays fraud score and decision
  • Shows submission timestamp
  • Provides link to Signifyd case dashboard
  • Manual order submission button

Meta Box Display:

add_action('add_meta_boxes', [$this, 'add_signifyd_meta_box']);

Displayed Data:

  • Signifyd Case ID
  • Fraud Score (0-1000)
  • Decision (APPROVED, REJECTED, HOLD)
  • Submission Date/Time
  • Link to Signifyd Dashboard

Settings Class

File: inc/class-settings.php
Namespace: \Suma\Signifyd\Settings

Manages plugin settings interface and option storage.

Hooks:

add_action('admin_menu', [$this, 'add_settings_page']);
add_action('admin_init', [$this, 'register_settings']);

Settings Sections:

  1. API Settings — Enable, keys, mode
  2. Fulfillment — Origin address, enable fulfillment
  3. Advanced — Email notifications, starting post ID
  4. WIP — Work in progress features

Field Registration:

register_setting('suma_signifyd_plugin_options', 'suma_signifyd_plugin_options');
add_settings_field('suma_signifyd_setting_api_key', 'Production API Key', [...]);

Session_Finger_Print Class

File: inc/class-session-finger-print.php
Namespace: \Suma\Signifyd\Session_Finger_Print

Generates device fingerprints for fraud detection.

Purpose:

  • Track unique browser sessions
  • Detect account takeover attempts
  • Identify suspicious patterns across orders

Methods:

  • get_fingerprint() — Generates browser fingerprint
  • store_fingerprint($order) — Saves to order meta

Log Class

File: inc/class-log.php
Namespace: \Suma\Signifyd\Log

Centralized logging functionality using WooCommerce logging system.

Log Files:

  • signifyd-for-woocommerce-{date}.log — API requests/responses
  • signifyd-for-woocommerce-webhooks-{date}.log — Webhook events

Methods:

  • log($message, $level) — Write log entry
  • log_api_request($request, $response) — API logging
  • log_webhook($data) — Webhook logging

Usage:

Log::log('Order submitted to Signifyd', 'info');
Log::log_api_request($payload, $response);

Data Flow

Order Submission Flow

┌─────────────────────┐
│ Customer Places │
│ Order │
└──────────┬──────────┘


┌─────────────────────┐
│ Payment Gateway │
│ Processes Payment │
└──────────┬──────────┘


┌─────────────────────┐
│ WooCommerce │
│ Status: Processing │
└──────────┬──────────┘


┌─────────────────────┐
│ Orders Class │
│ submit_order() │
└──────────┬──────────┘


┌─────────────────────┐
│ Payload Class │
│ build_payload() │
└──────────┬──────────┘


┌─────────────────────┐
│ cURL API Request │
│ POST to Signifyd │
└──────────┬──────────┘


┌─────────────────────┐
│ Signifyd API │
│ Analyzes Order │
└──────────┬──────────┘


┌─────────────────────┐
│ API Response │
│ {case_id, score} │
└──────────┬──────────┘


┌─────────────────────┐
│ Save Order Meta │
│ Fire Action Hooks │
└──────────┬──────────┘


┌─────────────────────┐
│ Log Request/Response│
└─────────────────────┘

Webhook Processing Flow

┌─────────────────────┐
│ Signifyd Decision │
│ Update │
└──────────┬──────────┘


┌─────────────────────┐
│ Webhook POST │
│ to WP REST API │
└──────────┬──────────┘


┌─────────────────────┐
│ Webhook_Routes │
│ handle_webhook() │
└──────────┬──────────┘


┌─────────────────────┐
│ Validate HMAC │
│ Signature │
└──────────┬──────────┘


┌─────────────────────┐
│ Parse Decision │
│ Extract Order ID │
└──────────┬──────────┘


┌─────────────────────┐
│ Update Order Status │
│ Based on Decision │
└──────────┬──────────┘


┌─────────────────────┐
│ Fire WordPress │
│ Action Hooks │
└──────────┬──────────┘


┌─────────────────────┐
│ Log Webhook Data │
│ Return HTTP 200 │
└─────────────────────┘

Database Schema

Order Meta Keys

The plugin stores Signifyd data as order meta:

Meta KeyDescriptionExample Value
signifyd_case_idSignifyd case identifier12345678
signifyd_scoreFraud score (0-1000)850
signifyd_decisionFraud decisionAPPROVED
signifyd_submitted_atSubmission timestamp2026-04-20 14:30:00
signifyd_fingerprintBrowser fingerprintabc123...
signifyd_fulfillment_sentFulfillment submitted1

Access in Code:

$case_id = get_post_meta($order_id, 'signifyd_case_id', true);

Constants

Defined in main plugin file:

ConstantDescriptionValue
SSROOTPlugin root directoryplugin_dir_path(__FILE__)
SSINCInclude directorySSROOT . 'inc/'
SUMA_SIGNIFYD_VERSIONPlugin version1.1.5
SUMA_SIGNIFYD_SETTINGSPlugin settings arrayget_option(...)

API Integration

Signifyd API Endpoints

Base URL: https://api.signifyd.com/v2/

Endpoints Used:

  • POST /cases — Submit new case
  • GET /cases/{caseId} — Get case details
  • POST /cases/{caseId}/fulfillments — Submit fulfillment

Authentication:

$headers = [
'Authorization: Basic ' . base64_encode($api_key . ':'),
'Content-Type: application/json'
];

cURL Request Example

$ch = curl_init('https://api.signifyd.com/v2/cases');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);

Security Considerations

HMAC Signature Validation

Webhooks use HMAC-SHA256 signatures:

$expected = hash_hmac('sha256', $body, $secret, false);
$received = $_SERVER['HTTP_X_SIGNIFYD_SEC_HMAC_SHA256'];

if (!hash_equals($expected, $received)) {
die('Invalid signature');
}

Nonce Verification

Settings form uses WordPress nonces:

wp_nonce_field('suma_signifyd_settings', 'suma_signifyd_nonce');

// Verify:
if (!wp_verify_nonce($_POST['suma_signifyd_nonce'], 'suma_signifyd_settings')) {
wp_die('Security check failed');
}

Capability Checks

Settings require admin capabilities:

if (!current_user_can('manage_options')) {
wp_die('Unauthorized');
}

Performance Considerations

  • Async Recommended: Consider using wp_schedule_single_event() for API submissions to avoid blocking checkout
  • Caching: Response data cached in order meta to avoid repeated API calls
  • Logging: Log files can grow large; consider rotation strategy
  • Webhook Processing: Fast response required (< 5 seconds)

Next Steps