Secure Exchanges SDK for PHP - Secure messaging, file exchange, and electronic signatures

Maintainers

Package info

github.com/Secure-exchanges/php-sdk

pkg:composer/secure-exchanges/sdk

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-08-12 15:26 UTC

This package is auto-updated.

Last update: 2026-08-12 17:50:11 UTC


README

Version française : README.fr.md

Secure Exchanges PHP SDK

PHP SDK for the Secure Exchanges API — end-to-end encrypted messaging, secure file transfer, electronic signature, and PKI certification of PDF documents.

With this SDK, from your PHP application you can:

  • send end-to-end encrypted messages and files, with or without a password, with SMS-based 2FA verification;
  • have PDF documents signed electronically (signature zones, initials, dates, text fields, and more);
  • certify PDFs with a digital certificate (PKI) before sending them for signature;
  • read, extend, and delete messages, and create secure reply envelopes;
  • view activity logs (sends, opens, signatures).

Table of contents

  1. Security
  2. Installation
  3. Requirements
  4. Credentials
  5. Configuration
  6. SDK architecture
  7. Usage guide
  8. API reference
  9. Support
  10. License

Security

  • End-to-end encryption: the content (subject, message body, files) is encrypted inside your application before it leaves your server, using keys generated locally and never transmitted to the Secure Exchanges server — the server only ever sees encrypted data.
  • Post-quantum key exchange: ML-KEM-1024 (FIPS 203), resistant to quantum-computer attacks, signed with ML-DSA-87 (FIPS 204).
  • AES-256 encryption of the content and of all exchanges with the server.
  • Interception protection: any man-in-the-middle (MITM) attack attempt causes the connection to fail.
  • Nothing to manage: the SDK automatically applies the entire protocol on every call — you never have to handle any cryptographic primitives yourself.

Installation

composer require secure-exchanges/sdk

Or from source:

cd phpSDK
composer install

Dependencies

Dependencies are managed by Composer (see composer.json):

  • paragonie/pqcrypto_compat (^0.3) — post-quantum cryptography (ML-KEM / ML-DSA)
  • phpseclib/phpseclib — cryptography (RSA for some auxiliary functions, utilities)
  • setasign/fpdi, setasign/fpdf — PDF manipulation
  • smalot/pdfparser — PDF reading
  • chrome-php/chrome — PKI certification (headless browser)
  • giggsey/libphonenumber-for-php — phone number validation

Requirements

To use the SDK, you need:

  • PHP 8.2 or newer (composer install fails on PHP 8.1)
  • PHP extensions: openssl, json, mbstring, curl
  • ext-sockets — required for PKI signing (used by chrome-php)
  • Google Chrome or Chromium installed on the machine — required only for PDF certification (PKI); the binary path can be provided via the SE_CHROME_PATH environment variable
  • A serial number (serial) — the UUID of your license
  • An API user ID (api_user) — the UUID of the API user
  • An API password (api_password) — the UUID of the API password

Recommended in production: the native ext-pqcrypto extension (github.com/paragonie/ext-pqcrypto) — significantly faster for post-quantum cryptography than the pure-PHP implementation installed by Composer. No code changes are needed: the SDK automatically switches to the extension as soon as it is loaded. Otherwise, enabling ext-opcache (with JIT) noticeably speeds up the pure-PHP implementation.

Credentials

Where to find your credentials

The 3 required credentials (serial, api_user, api_password) are UUIDs (GUIDs) that you obtain from your Secure Exchanges administration portal when creating your API license.

Credential Description
serial Serial number of your license
api_user API user ID
api_password API user password

Configuration

Configuration via .env

The SDK is configured through a .env file at the root of your project (add it to .gitignore — never commit it):

# .env
SE_SERIAL=d17ca972-5f70-4961-bac4-98e6b91b1dc4
SE_API_USER=0fa0cf77-4ca5-4406-8623-1ed0ffd47290
SE_API_PASSWORD=4bfeabdf-c498-448a-9255-1e17c3ef8c0b
SE_USE_PREVIEW=false      # true = test (preview) environment; production by default
SE_VERIFY_SSL=true        # keep true in both preview and production
SE_CULTURE=fr-CA
SE_CHROME_PATH=           # optional: Chrome/Chromium path (PKI certification)

SE_USE_PREVIEW=true points the SDK to the preview test environment; your preview and production credentials are different — a production serial will not work in preview, and vice versa.

SettingsHelper::configureFromEnv() does everything in one call: it loads the .env file (looked up in the current directory, then next to the main script, without ever overwriting environment variables that are already set), selects the environment, and reads the credentials:

use SecureExchanges\SDK\Client\SecureExchangesClient;
use SecureExchanges\SDK\Client\SEMSClient;
use SecureExchanges\SDK\Helpers\SettingsHelper;

// Returns ['apiEndpoint' => ..., 'semsEndpoint' => ...,
//          'environment' => 'production'|'preview']
$endpoints = SettingsHelper::configureFromEnv();

$client = new SecureExchangesClient($endpoints['apiEndpoint']);
$sems   = new SEMSClient($endpoints['semsEndpoint']);

// Credentials are available through the getters:
$serial      = SettingsHelper::getSerial();
$apiUser     = SettingsHelper::getApiUser();
$apiPassword = SettingsHelper::getApiPassword();
$culture     = SettingsHelper::getCulture();       // e.g. "fr-CA"
$verifySsl   = SettingsHelper::getVerifySsl();

You can also pass an explicit path: SettingsHelper::configureFromEnv('/path/to/.env').

SDK architecture

The SDK is organized into three main modules (namespace SecureExchanges\SDK, PSR-4 from src/):

  • Client/ — the HTTP layer: SecureExchangesClient (main API), SEMSClient (email and key delivery service), both built on BaseClient.
  • Helpers/ — the business logic: MessageHelper (send/read/delete), HandShakeHelper (post-quantum handshake), CryptoHelper (AES/RSA/SHA primitives), SignHelper and ZoneBuilder (signature zones), PkiFileHelper (PDF certification), FileHelper (encrypted upload/download), SettingsHelper (configuration), plus validation helpers (EmailHelper, PhoneHelper), logging (LogsHelper), and licensing (LicenceHelper).
  • Models/ — the typed objects: responses (Answer/), entities (Entity/), enumerations (Enum/), transport objects (Transport/), JSON models (JsonModels/), arguments (Args/). The Callback/ folder contains the notification (callback) objects.

Usage guide

All the examples below assume the following setup:

use SecureExchanges\SDK\Client\SecureExchangesClient;
use SecureExchanges\SDK\Client\SEMSClient;
use SecureExchanges\SDK\Helpers\SettingsHelper;
use SecureExchanges\SDK\Helpers\MessageHelper;
use SecureExchanges\SDK\Models\Entity\RecipientInfo;

$endpoints = SettingsHelper::configureFromEnv();

$client = new SecureExchangesClient($endpoints['apiEndpoint']);
$sems   = new SEMSClient($endpoints['semsEndpoint']);

$serial      = SettingsHelper::getSerial();
$apiUser     = SettingsHelper::getApiUser();
$apiPassword = SettingsHelper::getApiPassword();

Sending a simple message

$answer = MessageHelper::multiRecipientMessage(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    recipients:  [new RecipientInfo(email: 'recipient@example.com')],
    message:     'Hello, here is your secure message.',
    subject:     'Confidential message',
    cultureId:   'en-US',
    semsClient:  $sems
);

if ($answer->status === 200 && $answer->recipientsAnswer) {
    echo 'URL: ' . $answer->recipientsAnswer[0]->answer->url;
}

Sending with a password and SMS 2FA

The password and the delivery channel are two independent, combinable options. The channel is controlled by the SendMethodEnum enumeration:

Value Behavior
SendMethodEnum::ONLY_EMAIL (default) Link sent by email
SendMethodEnum::SMS_ONLY Link sent by SMS only
SendMethodEnum::SMS_WITH_EMAIL_CODE Link by SMS, opening code by email
SendMethodEnum::EMAIL_WITH_SMS_CODE Link by email, opening code by SMS (2FA)
use SecureExchanges\SDK\Models\Enum\SendMethodEnum;

$answer = MessageHelper::multiRecipientMessage(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    recipients:  [new RecipientInfo(email: 'user@example.com', phone: '+15145551234')],
    message:     'Confidential document attached.',
    subject:     'Protected document',
    password:    'SecretPassword123',
    sendMethod:  SendMethodEnum::EMAIL_WITH_SMS_CODE,
    cultureId:   'en-US',
    maximumOpenTime:   3,     // maximum number of opens
    minutesExpiration: 1440,  // expiration in minutes (1440 = 24 h)
    semsClient:  $sems
);

Sending with attachments

// From disk
$answer = MessageHelper::multiRecipientMessage(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    recipients:  [new RecipientInfo(email: 'recipient@example.com')],
    message:     'See attached files.',
    subject:     'Secure files',
    filesPath:   ['/path/to/document.pdf', '/path/to/image.png'],
    semsClient:  $sems
);

// From memory
$answer = MessageHelper::multiRecipientMessage(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    recipients:  [new RecipientInfo(email: 'recipient@example.com')],
    message:     'Report and data attached.',
    subject:     'Monthly report',
    filesList:   [
        ['binary' => $pdfBytes, 'file_name' => 'report.pdf'],
        ['binary' => $csvBytes, 'file_name' => 'data.csv'],
    ],
    semsClient:  $sems
);

Both parameters (filesPath and filesList) can be combined in the same send.

Sending with PKI certification

PKI certification applies a digital certificate to the PDF before it is sent for signature. It requires Chrome/Chromium (headless browser via chrome-php) and the ext-sockets extension. Signature zones are defined with ZoneBuilder, which reads the actual dimensions of the PDF pages. The flow:

use SecureExchanges\SDK\Helpers\CryptoHelper;
use SecureExchanges\SDK\Helpers\ZoneBuilder;
use SecureExchanges\SDK\Helpers\SignHelper;
use SecureExchanges\SDK\Helpers\PkiFileHelper;
use SecureExchanges\SDK\Models\Args\CertifyPdfArgs;
use SecureExchanges\SDK\Models\Args\FileArgs;
use SecureExchanges\SDK\Models\Entity\SignZoneDefinition;
use SecureExchanges\SDK\Models\Transport\FileZoneDefinition;
use SecureExchanges\SDK\Models\Transport\RecipientZoneDefinition;

// 1. Read the PDF and compute its SHA512
$pdfPath  = '/path/to/contract.pdf';
$pdfBytes = file_get_contents($pdfPath);
$sha512   = CryptoHelper::getSha512HashOfBytes($pdfBytes);

// 2. Define the signature zones with ZoneBuilder
$zoneBuilder = new ZoneBuilder($pdfPath, isPki: true);
$zones = [
    // Signature zone on page 1
    $zoneBuilder->createFieldSignZone(
        page: 1, fieldName: 'signature1',
        x: 60, y: 400, width: 200, height: 50
    ),
    // Date zone filled in automatically at signing time
    $zoneBuilder->createFieldDateSignZone(
        page: 1, fieldName: 'date1',
        x: 60, y: 500, width: 100, height: 25, fontSize: 12
    ),
];
$zoneBuilder->close();

// 3. Bind the zones to the recipient (file referenced by its SHA512)
$recipientZones = [
    new RecipientZoneDefinition(
        email: 'signer@example.com',
        phone: null,
        zonesDefByFile: [
            new FileZoneDefinition(
                uniqueName: $sha512,
                zonesDef: new SignZoneDefinition(mode: 'define', zones: $zones),
                doNotEncryptPDF: false,
                doNotAppendCertificateToFile: false,
                isPKIFile: true
            )
        ]
    )
];

// 4. Get the certification token
$tokenResponse = SignHelper::getCertificationToken(
    apiClient:    $client,
    serial:       $serial,
    apiUser:      $apiUser,
    apiPassword:  $apiPassword,
    originalHash: $sha512,
    cultureId:    'en-US',
    tokenType:    'CertifyDocument'
);
$certData = $tokenResponse['certification_data'];

// 5. Certify the PDF (headless browser)
$certifyArgs = new CertifyPdfArgs(
    file:                     new FileArgs($pdfBytes, 'contract.pdf'),
    recipients:               ['signer@example.com'],
    certifyData:              $certData,
    detectExistingFields:     true,
    recipientZoneDefinitions: $recipientZones
);

$pkiHelper    = new PkiFileHelper(isPreview: false, culture: 'en-US');
$certifiedPdf = $pkiHelper->certifyPkiPdf($certifyArgs);
$pkiHelper->close();

// 6. Send the certified PDF
$answer = MessageHelper::multiRecipientMessage(
    apiClient:     $client,
    serial:        $serial,
    apiUser:       $apiUser,
    apiPassword:   $apiPassword,
    recipients:    [new RecipientInfo(email: 'signer@example.com')],
    message:       'Please sign this certified document.',
    subject:       'PKI document to sign',
    cultureId:     'en-US',
    certifiedPdfs: [$certifiedPdf],
    semsClient:    $sems
);

A complete, runnable example — including signature options (SignOptions: restricting the signing mode, requesting a file in return) and callback parameters (contextual keys) — can be found in example_pki_with_signature_options.php at the root of phpSDK/.

Retrieving a message

// Parse the received link
$msgParams = MessageHelper::getSecureExchangesMessageFromLink($url);

// Decrypt the message
$response = MessageHelper::getMessage(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    msgParams:   $msgParams,
    password:    null,        // password if the message is protected
    digitCode:   null,        // SMS code if 2FA is enabled
    semsClient:  $sems
);

if ($response->status === 200) {
    echo "Subject: {$response->subject}\n";
    echo "Message: {$response->message}\n";
    // $response->filesMetaData, $response->remainingOpenTime, $response->expireOn, ...
}

Note: each read consumes one of the message's allowed opens (maximumOpenTime).

Deleting a message

$success = MessageHelper::deleteMessageByTrackingId(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    trackingId:  'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
    cultureId:   'en-US'
);

Reply envelopes

Envelopes let a recipient send documents to you securely:

// Simple envelope (single recipient)
$envelope = MessageHelper::getEnveloppe(
    apiClient:            $client,
    serial:               $serial,
    apiUser:              $apiUser,
    apiPassword:          $apiPassword,
    subject:              'Send me your documents',
    destination:          'recipient@example.com',
    cultureId:            'en-US',
    replyExpirationHours: 48,
    maximumReplyOpenTime: 3,
    replyToApi:           true,
    authorizedExtensions: ['.pdf', '.docx']
);

if ($envelope->url) {
    echo "Envelope URL: {$envelope->url}";
}

// Multiple envelopes (one per recipient; a phone number on the
// recipient automatically enables 2FA)
$envelopes = MessageHelper::getEnvelopes(
    apiClient:   $client,
    serial:      $serial,
    apiUser:     $apiUser,
    apiPassword: $apiPassword,
    subject:     'Required documents',
    recipients:  [
        ['Email' => 'user1@example.com'],
        ['Email' => 'user2@example.com', 'Phone' => '+15145551234'],
    ]
);

API reference

MessageHelper

Static class for sending and receiving messages (src/Helpers/MessageHelper.php).

Method Description
multiRecipientMessage(...) Sends a message to one or more recipients
getMessage($apiClient, $serial, $apiUser, $apiPassword, $msgParams, $password = null, $digitCode = null, $semsClient = null) Retrieves and decrypts a message
getSecureExchangesMessageFromLink($link) Parses a Secure Exchanges link into parameters for getMessage
deleteMessageByTrackingId($apiClient, $serial, $apiUser, $apiPassword, $trackingId, $cultureId = 'en-US') Deletes a message
getEnveloppe(...) Creates a reply envelope (single recipient)
getEnvelopes(...) Creates multiple envelopes
extendMessageLive($apiClient, $messageId, $extendMessageToken) Extends the lifetime of a message being read
sendTrace($apiClient, $traceContent) Sends a support trace

multiRecipientMessage parameters

Parameter Type Default Description
apiClient SecureExchangesClient required API client
serial string required Serial number (UUID)
apiUser string required API user ID (UUID)
apiPassword string required API password (UUID)
recipients RecipientInfo[] required Recipients
message string required Message content (HTML accepted)
subject string required Subject
password ?string null Protection password
filesList ?array null In-memory files (['binary' => ..., 'file_name' => ...])
filesPath ?array null Paths of files on disk
sendMethod SendMethodEnum ONLY_EMAIL Delivery channel (see table above)
getBackHtml bool true true = you send the email yourself, false = Secure Exchanges sends it
showSubject bool true Show the subject in the notification
getNotify bool true Notify when the message is opened
cultureId string 'en-US' Language ('fr-CA', 'en-US', …)
maximumOpenTime int 1 Maximum number of opens
minutesExpiration int 10080 Expiration in minutes (default: 7 days)
createMessageCopy bool false Copy for the sender
name string 'Secure Exchanges' Sender name
callbackParameters ?string null Callback parameters (JSON)
replyOptions ?array null Reply options
ownerDontNeedToSign bool true The sender does not sign
semsClient ?SEMSClient null SEMS client (created automatically if omitted)
certifiedPdfs ?array null PKI-certified PDFs (CertifiedPdfContainer)
signOptions ?SignOptions null Signature options (restriction, return upload)

Returns: MultiRecipientAnswer (status, data, recipientsAnswer[] — each element contains a SendMessageAnswer with url, trackingId, htmlMsg, …).

SignHelper

Signature zone and certification management (src/Helpers/SignHelper.php).

Method Description
getCertificationToken($apiClient, $serial, $apiUser, $apiPassword, $originalHash, $cultureId, $tokenType = null, $metadata = null) Obtains a PKI certification token
addFileZoneDefToRecipientIndex(...) Converts templates into zone definitions
convertRecipientIndexToList($recipientIndex) Converts the index into a list for multiRecipientMessage
concatRecipientZoneDefinitionLists($list1, $list2) Merges two zone definition lists
validateAllRecipientsHaveZones(...) Checks that all recipients have zones
validateDocumentSignatureIntegrity(...) Validates the integrity of a signed document
applyPKI($manifest, $certificationData) Applies the certification data to a manifest

PkiFileHelper

PKI certification of PDFs via headless browser (src/Helpers/PkiFileHelper.php, based on chrome-php).

$helper = new PkiFileHelper(isPreview: false, culture: 'en-US');
$certified = $helper->certifyPkiPdf($args);   // CertifyPdfArgs -> CertifiedPdfContainer
$helper->close();

The Chrome/Chromium binary is detected automatically, or forced via the SE_CHROME_PATH environment variable.

CertifyPdfArgs: file (FileArgs: binary + name), recipients (emails), certifyData (certification token), detectExistingFields (default true), recipientZoneDefinitions, defineRecipients (optional callback).

CertifiedPdfContainer (return value): fileBytes (certified PDF), fileName, signFilesRequired (including the new sha512), recipientZoneDefinitions (zones updated with the new hash).

ZoneBuilder

Builds signature zones from a PDF (src/Helpers/ZoneBuilder.php).

$builder = new ZoneBuilder($filePath, isPki: true);
$zone = $builder->createFieldSignZone(page: 1, fieldName: 'sig1', x: 50, y: 600, width: 200, height: 80);
$builder->close();

Zone types: signature, initials, text, number, date, signing date, checkbox, radio buttons, text area, option list, dropdown list. Utilities: getPageCount(), getPageDimensions($page).

SettingsHelper

SDK configuration (src/Helpers/SettingsHelper.php).

Method Description
configureFromEnv($envFile = null) Loads .env, selects the environment, reads the credentials
loadEnv($envFile = null) Loads a .env file without overwriting the existing environment
getSerial() / getApiUser() / getApiPassword() Configured credentials
getLicenceEmail() / getCulture() / getVerifySsl() Configured options
getSemsEndpoint() / getFileUploadHandler() / getFileDownloadHandler() Effective endpoints
reset() Resets to default values

Other helpers

Class Purpose
LogsHelper getLog(...) (by messageId or trackingId), getLogs($apiClient, $serial, $apiUser, $apiPassword, $fromDate, $toDate, $userLogSerial = null)
LicenceHelper getSerialInfo(...), isLicenceValid(...) — license information and validity
CryptoHelper Primitives: getSha512HashOfBytes, getSha512OfFile, AES (encryptBinary/decryptBinaryFromBytes, …), RSA, .NET GUID (guidToBytesLe)
EmailHelper / PhoneHelper Validation of email addresses and phone numbers (E.164, SMS capability)

Data models

// Recipient — a phone number enables the SMS/2FA features
new RecipientInfo(email: 'user@example.com', phone: '+15145551234');

GetMessageResponse (reading): status, subject, message, filesMetaData, remainingOpenTime, expireOn, createdDate, callbackParameters, containsSignsFiles, …

GetEnveloppeResponse (envelope): url, statusCode, repId, recipient.

Support

For any question or issue, contact supportdev@secure-exchanges.info or visit secure-exchanges.com. Product documentation: help.secure-exchanges.com.