Electronic signature
Principle diagram

Functioning
The Electronic Signature API offers a subset of methods (called “externals”) for any electronic signature integration.
We need to have an external site that Retail operations will call up.
The iframe will need to connect to our system with a dedicated user.
Please note the user must be from an external profile.
From this site it will have to communicate between the electronic signature provider and Retail operations to update the signed objects (e.g. a document).
We need to have this URL in our database in order to be able to call you.
Example:
https://YOUR_URL/?entityTypeID={entityTypeID}&entityID={entityID}&storeID={storeID}&objectTypeID={objectTypeID}&processID={processID}&localisation={localisation}&winLangID={winLangID}
The url will be called with several parameters :
- entityTypeID --> Type of item to be signed (Quotation, Order, ...)
- entityID --> Identifier of the object to be signed
- storeID --> Identifier of the store related with the object to be signed
- objectTypeID --> Object to be signed (3 = Document)
- processID --> Signature process identifier
- localisation --> Retail operations start-up language (fr/en/sp, ...)
- winLangID --> Windows Language Identifier
To create a process with a provider you will need to ‘content’ the PDF document in base 64 and send it to them.
They will also need the details of the signatory (the project customer) in order to sign with valid information, in particular the telephone number.
Some providers require specific formats.
For the phone, for example, they may request the e.164 format. Retail operations doesn't handle this, so you'll have to manage it and send an explicit error message to Retail operations users in your window.
All the information required in order to create an electronic signature will be returned using the Retail operations Signature creation details API entry point.
You can also create project comments showing the progress of the electronic signature by calling the Create Project Comment API method.
You will also need to create the process in Retail operations, by calling the Create document status API method.
We're going to store the name of your provider for logging. It's a string of 30 characters long.
Sign the object by calling the Signed document API method.
When you go to sign the document, you can send us (in Byte array) the document signed with your provider. We will link it to the Retail operations document as an attachment.
Or cancel the process by calling the Delete process API method.
Supported postMessages
In the iframe there is an "OK" button which refreshes the document and closes the window.
There is also a "Manual Signature" button for signing a document without an electronic signature.
Please note that if you create a procedure in the iframe, we won't have any information on how to deactivate the "Manual signature" button.
The same goes for cancelling, which will not reactivate the button.
You can, however, use two postMessages, which allow you to block and unblock the two window buttons.
In order to do this, the postMessage needs an object with a MessageType: lockButton or unlockButton. Look the example.
The “Ok” and “Manual Signature” button. The "lock" will allow you to lock these two buttons, this will prevent you from leaving the Retail operations window.
Particularly when you start creating a procedure. The “unlock” message will reactivate these buttons and give control back to the seller.
We have other postMessages so you can customize your signature window:
- MessageType
setTitle: translate the window title, with the "Title" property being a string, for example: "Window Title" - MessageType
labelButtonOK: translate the button and customize its icon, with the following properties:- 'LabelButton': string, for example: 'Button Title'
- 'IconButton': string, for example: 'fa fa-ICON'. You must use the fontAwesome library (https://fontawesome.com/)
- MessageType
screenSize: to customize the size (height/width) of the window, using the properties (you will then need to manage the responsiveness of your window):- 'Width': string in percentage or pixels, for example: "90%" or "750px"
- 'Height': string in percentage or pixels, for example: "90%" or "750px"
- Please note, if the size exceeds 100% of your screen in width or height, it will exceed the screen and you will be stuck on this window. Same for under or equal to 0%.
When customizing labels, you will need to manage the translation by yourself.
If postMessages are not used, RetailOps will use its default values.
Please note that using these postMessages could have a visual impact when the page is opened.

Example (with YouSign)
Description of the example
The example is in PHP and HTML.
If you want to use our example you need to change multiple keys in the script.
Below are the details of the values corresponding to these parameters:
PHP :
{PROVIDER_URL} --> Provider URL (SANDBOX or PROD)
{PROVIDER_API_KEY} --> Provider API key
{URL_SMO_WEBSITE} --> https://SITE.storemanager.online/SITE/
{SMO_KEY} --> Access key provided by Dassault Systèmes
{LOGIN} --> Authentication login (Profile: External) provided by Dassault Systèmes
{PASSWORD} --> Authentication password (Profile: External) provided by Dassault Systèmes
{AUTH_SMO} --> Client ID provided by Dassault Systèmes
{DEFAULT_LOCALISATION} --> Default Localisation
{YOUR_PROVIDER} --> The name of the provider used (ex: YouSign)
HTML :
{FA_API_KEY} Font Awesome Api Key
PHP Sample
<?php
function authSMO($authToken, $authClientId){
$curl = curl_init();
$urlRESTGetInfos = sprintf("%sRESTWS/api/auth/v1/connectAutoWithAuthToken/?authToken=%s&authClientID=%s", $GLOBALS['url'], $authToken, $authClientId);
curl_setopt_array($curl, [
CURLOPT_URL=> $urlRESTGetInfos,
CURLOPT_RETURNTRANSFER=> true
]);
$response = curl_exec($curl);
if ($response === false) {
error_log("CURl Error: " . curl_error($curl));
return false;
}
$content = json_decode($response, true);
$GLOBALS['sessid'] = $content['data']['token'];
curl_close($curl);
return true;
}
function getInfosSignature($objectTypeID, $entityTypeID, $entityID){
$curl = curl_init();
$urlRESTGetInfos = sprintf("%sRESTWS/api/externals/v1/signature/%s/%s/%s/creationDetails/?winLangID=%s", $GLOBALS['url'], $objectTypeID, $entityTypeID, $entityID, $GLOBALS["winLangID"]);
curl_setopt_array($curl, [
CURLOPT_URL=> $urlRESTGetInfos,
CURLOPT_RETURNTRANSFER=> true,
CURLOPT_HTTPHEADER=> ['X-PHP-SESSID:' . $GLOBALS['sessid'],
'Content-Type: application/json']
]);
$response = curl_exec($curl);
$content = json_decode($response, true);
$success = $content['success'];
if($success == true){
$contentFile = $content['signatureDetails']['content'];
$nameFile = $content['signatureDetails']['name'];
$lastNameSigner = trim($content['signatureDetails']['lastname']);
$firstNameSigner = trim($content['signatureDetails']['firstName']);
$phoneSigner = str_replace(' ', '', $content['signatureDetails']['phone']); // Need format e.164
$emailSigner = trim($content['signatureDetails']['email']);
$followerEmail = trim($content['signatureDetails']['sellerEmail']);
$documentName = trim($content['signatureDetails']['documentName']);
if(empty($lastNameSigner || $lastNameSigner == "")){
echo("LASTNAMEFAILURE");
return false;
} else if(empty($firstNameSigner) || $firstNameSigner == "" ){
echo("FIRSTNAMEFAILURE");
return false;
} else if(!preg_match("#^\+[0-9]{3,15}$#", $phoneSigner)){
echo "PHONEFAILURE";
return false;
} else if(!filter_var($emailSigner, FILTER_VALIDATE_EMAIL)) {
echo("MAILFAILURE");
return false;
} else {
YouSignProcedureV3($contentFile, $nameFile, $lastNameSigner, $firstNameSigner, $phoneSigner, $emailSigner, $followerEmail, $documentName);
return true;
}
}
curl_close($curl);
}
function YouSignProcedureV3($contentFile, $nameFile, $lastNameSigner, $firstNameSigner, $phoneSigner, $emailSigner, $followerEmail, $documentName){
$curl = curl_init();
$nameSignature = "Signature " . $documentName;
$data = <<<JSON
{
"name": "{$nameSignature}",
"delivery_mode": "email",
"timezone": "Europe/Paris"
}
JSON;
curl_setopt_array($curl, [
CURLOPT_URL => sprintf('%s/signature_requests', $GLOBALS['youSignUrl']),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $data,
CURLOPT_HTTPHEADER => [
sprintf('Authorization: Bearer %s', $GLOBALS['youSignApiKey']),
'Content-Type: application/json'
]
]);
$initiateSignatureRequestResponse = curl_exec($curl);
$signatureRequest = json_decode($initiateSignatureRequestResponse, true);
$GLOBALS["signatureID"] = $signatureRequest['id'];
YouSignProcedureDocumentToProcedure($contentFile, $nameFile, $lastNameSigner, $firstNameSigner, $phoneSigner, $emailSigner, $followerEmail);
curl_close($curl);
}
function count_pdf_pages($pdftext){
$num = preg_match_all("/\/Page\W/", $pdftext, $dummy);
return $num;
}
function YouSignProcedureDocumentToProcedure($contentFile, $nameFile, $lastNameSigner, $firstNameSigner, $phoneSigner, $emailSigner, $followerEmail){
$temp = tmpfile();
$base64Decode = base64_decode($contentFile);
fwrite($temp, $base64Decode);
fseek($temp, 0);
$numberPages = count_pdf_pages($base64Decode);
$curl = curl_init();
curl_setopt_array(
$curl,
[
CURLOPT_URL => sprintf('%s/signature_requests/%s/documents', $GLOBALS['youSignUrl'], $GLOBALS["signatureID"]),
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS =>
[
'file' => new CURLFile(stream_get_meta_data($temp)['uri'], 'application/pdf', $nameFile),
'nature' => 'signable_document'
],
CURLOPT_HTTPHEADER => [
sprintf('Authorization: Bearer %s', $GLOBALS['youSignApiKey'])
]
]);
$documentUploadResponse = curl_exec($curl);
$document = json_decode($documentUploadResponse, true);
$documentId = $document['id'];
YouSignProcedureSigner($documentId, $lastNameSigner, $firstNameSigner, $phoneSigner, $emailSigner, $followerEmail, $numberPages);
curl_close($curl);
fclose($temp);
}
function YouSignProcedureSigner($documentId, $lastNameSigner, $firstNameSigner, $phoneSigner, $emailSigner, $followerEmail, $numberPages){
$curl = curl_init();
$arrayFields = array();
for($i=0; $i < $numberPages; $i++){
$arrayFields[$i] = array(
"document_id" => $documentId,
"type" => "signature",
"page" => $i+1,
"width" => 121,
"height" => 37,
"x" => 474,
"y" => 805
);
};
$arrayFieldsJson = json_encode($arrayFields);
$data = <<<JSON
{
"info": {
"first_name": "{$firstNameSigner}",
"last_name": "{$lastNameSigner}",
"email": "{$emailSigner}",
"phone_number": "{$phoneSigner}",
"locale": "{$GLOBALS["localisation"]}"
},
"signature_authentication_mode": "otp_sms",
"signature_level": "electronic_signature",
"fields": [
{
"document_id": "{$documentId}",
"type": "signature",
"page": 1,
"width": 121,
"height": 37,
"x": 474,
"y": 805
}
]
}
JSON;
curl_setopt_array($curl, [
CURLOPT_URL => sprintf('%s/signature_requests/%s/signers', $GLOBALS['youSignUrl'], $GLOBALS["signatureID"]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $data,
CURLOPT_HTTPHEADER => [
sprintf('Authorization: Bearer %s', $GLOBALS['youSignApiKey']),
'Content-Type: application/json'
]
]);
$addSignerResponse = curl_exec($curl);
$signer = json_decode($addSignerResponse, true);
$GLOBALS['signerID'] = $signer['id'];
YouSignProcedureFollower($followerEmail);
curl_close($curl);
}
function YouSignProcedureFollower($followerEmail){
$curl = curl_init();
$data = <<<JSON
[{
"email": "{$followerEmail}",
"locale": "{$GLOBALS["localisation"]}"
}]
JSON;
curl_setopt_array($curl, [
CURLOPT_URL => sprintf('%s/signature_requests/%s/followers', $GLOBALS['youSignUrl'], $GLOBALS["signatureID"]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $data,
CURLOPT_HTTPHEADER => [
sprintf('Authorization: Bearer %s', $GLOBALS['youSignApiKey']),
'Content-Type: application/json'
]
]);
$addSignerResponse = curl_exec($curl);
YouSignProcedureActivate();
curl_close($curl);
}
function YouSignProcedureActivate(){
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => sprintf('%s/signature_requests/%s/activate', $GLOBALS['youSignUrl'], $GLOBALS["signatureID"]),
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => false,
CURLOPT_HTTPHEADER => [
sprintf('Authorization: Bearer %s', $GLOBALS['youSignApiKey']),
'Content-Type: application/json'
]
]);
$activateSignatureRequestResponse = curl_exec($curl);
$process = json_decode($activateSignatureRequestResponse, true);
curl_close($curl);
}
function projectComment($objectTypeID, $entityTypeID, $entityID, $status){
$curl = curl_init();
$customMsg = "";
$urlCreateProjectComment = sprintf("%sRESTWS/api/externals/v1/signature/%s/%s/%s/%s/projectComment/?kindMessage=%s&winLangID=%s"
, $GLOBALS['url']
, $objectTypeID
, $entityTypeID
, $entityID
, $GLOBALS["signatureID"]
, $status
, $GLOBALS["winLangID"]
);
curl_setopt_array($curl, [
CURLOPT_URL => $urlCreateProjectComment,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $customMsg ,
CURLOPT_HTTPHEADER => ['X-PHP-SESSID:' . $GLOBALS['sessid'],
'Content-Type: application/json']
]);
$response = curl_exec($curl);
curl_close($curl);
}
function createProcess($objectTypeID, $entityTypeID, $entityID){
$curl = curl_init();
$urlSetStatusDocument = sprintf("%sRESTWS/api/externals/v1/signature/%s/%s/%s/%s/?provider=%s&winLangID=%s"
, $GLOBALS['url']
, $objectTypeID
, $entityTypeID
, $entityID
, $GLOBALS["signatureID"]
, $GLOBALS["providerName"]
, $GLOBALS["winLangID"]
);
curl_setopt_array($curl, [
CURLOPT_URL => $urlSetStatusDocument,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => '',
CURLOPT_HTTPHEADER => ['X-PHP-SESSID:' . $GLOBALS['sessid'],
'Content-Type: application/json']
]);
$response = curl_exec($curl);
curl_close($curl);
}
function cancelProcess($objectTypeID, $entityTypeID, $entityID){
$curl = curl_init();
$urlSetStatusDocument = sprintf("%sRESTWS/api/externals/v1/signature/%s/%s/%s/%s/?provider=%s&winLangID=%s"
, $GLOBALS['url']
, $objectTypeID
, $entityTypeID
, $entityID
, $GLOBALS["signatureID"]
, $GLOBALS["providerName"]
, $GLOBALS["winLangID"]
);
curl_setopt_array($curl, [
CURLOPT_URL => $urlSetStatusDocument,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_POSTFIELDS => '',
CURLOPT_HTTPHEADER => ['X-PHP-SESSID:' . $GLOBALS['sessid'],
'Content-Type: application/json']
]);
$response = curl_exec($curl);
curl_close($curl);
}
function YouSignCancelProcess(){
$curl = curl_init();
$data = <<<JSON
{
"custom_note": "string",
"reason": "contractualization_aborted"
}
JSON;
curl_setopt_array($curl, [
CURLOPT_URL => sprintf('%s/signature_requests/%s/cancel', $GLOBALS['youSignUrl'], $GLOBALS["signatureID"]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $data,
CURLOPT_HTTPHEADER => [
sprintf('Authorization: Bearer %s', $GLOBALS['youSignApiKey']),
'Content-Type: application/json'
]
]);
$cancelResponse = curl_exec($curl);
$cancel = json_decode($cancelResponse, true);
curl_close($curl);
}
function signedDocumentYouSign($objectTypeID, $entityTypeID, $entityID){
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => sprintf('%s/signature_requests/%s/documents/download', $GLOBALS['youSignUrl'], $GLOBALS["signatureID"]),
CURLOPT_RETURNTRANSFER=> true,
CURLOPT_HTTPHEADER => [
sprintf('Authorization: Bearer %s', $GLOBALS['youSignApiKey']),
'Content-Type: application/pdf'
]
]);
$getDocumentResponse = curl_exec($curl);
signedDocument($objectTypeID, $entityTypeID, $entityID, $getDocumentResponse);
curl_close($curl);
}
function signedDocument($objectTypeID, $entityTypeID, $entityID, $fileToSave){
$curl = curl_init();
$urlSigned = sprintf("%sRESTWS/api/externals/v1/signature/%s/%s/%s/%s/signed/?provider=%s&winLangID=%s"
, $GLOBALS['url']
, $objectTypeID
, $entityTypeID
, $entityID
, $GLOBALS["signatureID"]
, $GLOBALS["providerName"]
, $GLOBALS["winLangID"]
);
curl_setopt_array($curl, [
CURLOPT_URL => $urlSigned,
CURLOPT_RETURNTRANSFER => false,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_POSTFIELDS => $fileToSave,
CURLOPT_HTTPHEADER => ['X-PHP-SESSID:' . $GLOBALS['sessid'],
'Content-Type: application/json']
]);
$response = curl_exec($curl);
curl_close($curl);
}
function sendMailAgain(){
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => sprintf('%s/signature_requests/%s/signers/%s/send_reminder', $GLOBALS['youSignUrl'], $GLOBALS["signatureID"], $GLOBALS["signerID"]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
sprintf('Authorization: Bearer %s', $GLOBALS['youSignApiKey']),
'Content-Type: application/json'
]
]);
$addSignerResponse = curl_exec($curl);
$signer = json_decode($addSignerResponse, true);
curl_close($curl);
}
function getInfosProcess(){
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => sprintf('%s/signature_requests/%s', $GLOBALS['youSignUrl'], $GLOBALS["signatureID"]),
CURLOPT_RETURNTRANSFER => false,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => [
sprintf('Authorization: Bearer %s', $GLOBALS['youSignApiKey']),
'Content-Type: application/json'
]
]);
$signatureResponse = curl_exec($curl);
$signature = json_decode($signatureResponse, true);
curl_close($curl);
}
function openProcess(){
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => sprintf('%s/signature_requests/%s/signers/%s', $GLOBALS['youSignUrl'], $GLOBALS["signatureID"], $GLOBALS["signerID"]),
CURLOPT_RETURNTRANSFER => false,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => [
sprintf('Authorization: Bearer %s', $GLOBALS['youSignApiKey']),
'Content-Type: application/json'
]
]);
$signerResponse = curl_exec($curl);
$signer = json_decode($signerResponse, true);
curl_close($curl);
}
/**
* Create an Dassault System connection token valid for 1 minute
* @param [in] $login user login
* @param [in] $password user password
* @param [in] $key encryption key
* @return auth token
*/
function createSpiAuthToken($login, $password, $key) {
// retrieve the current date in UTC format
$UTC = new DateTimeZone("UTC");
$currDate = new DateTime("now", $UTC);
$token = $currDate->format(DATE_ISO8601).'|'.$login.'|'.$password.'|';
if (version_compare(phpversion(), '7.2.0', '<')) {
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$padding = $block - (strlen($token) % $block);
$token .= str_repeat(chr($padding), $padding);
return base64_url_encode(
mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
base64_decode($key),
$token,
MCRYPT_MODE_CBC,
base64_decode("T0iPmNI7v+XCi7tSy8npYA==")
)
);
} else {
return base64_url_encode(
openssl_encrypt(
$token,
'aes-256-cbc',
base64_decode($key),
$options=OPENSSL_RAW_DATA,
base64_decode("T0iPmNI7v+XCi7tSy8npYA==")
)
);
}
}
// base64_encode URL friendly
function base64_url_encode($input) {
return strtr(base64_encode($input), '+/=', '-_,');
}
$youSignUrl = "PROVIDER_URL"; // SANDBOX OR PROD
$youSignApiKey = 'PROVIDER_API_KEY';
$url = "URL_SMO_WEBSITE";
$key = "SMO_KEY";
$login = "LOGIN";
$password = "PASSWORD";
$authClientId = "AUTH_SMO";
$GUID = '';
$localisation = "DEFAULT_LOCALISATION";
$signatureID = '';
$signerID = '';
$token = createSpiAuthToken($login, $password, $key);
$sessid = '';
$winLangID = -1;
$providerName = "YOUR_PROVIDER"; // Care only 30 char in data base
if (isset($_GET["action"]))
{
switch ($_GET["action"])
{
case 'createProcedure':
if(authSMO($token, $authClientId)){
$objectTypeID = $_GET["ObjectTypeID"];
$entityTypeID = $_GET["EntityTypeID"];
$entityID = $_GET["EntityID"];
$GLOBALS["localisation"] = $_GET["Localisation"];
$GLOBALS["winLangID"] = $_GET["WinLangID"];
if(getInfosSignature($objectTypeID, $entityTypeID, $entityID)){
if(isset($GLOBALS["signatureID"]) && isset($GLOBALS["signerID"])){
$status = 0;
projectComment($objectTypeID, $entityTypeID, $entityID, $status);
$status = "Pending";
createProcess($objectTypeID, $entityTypeID, $entityID);
}
}
}
break;
case 'iframeProcedure':
if(authSMO($token, $authClientId)){
$objectTypeID = $_GET["ObjectTypeID"];
$entityTypeID = $_GET["EntityTypeID"];
$entityID = $_GET["EntityID"];
$GLOBALS["localisation"] = $_GET["Localisation"];
$GLOBALS["winLangID"] = $_GET["WinLangID"];
if(getInfosSignature($objectTypeID, $entityTypeID, $entityID)){
if(isset($GLOBALS["signatureID"]) && isset($GLOBALS["signerID"])){
$status = 0;
projectComment($objectTypeID, $entityTypeID, $entityID, $status);
$status = "Pending";
createProcess($objectTypeID, $entityTypeID, $entityID);
}
}
}
break;
case 'sendMailAgain':
if(authSMO($token, $authClientId)){
$GLOBALS["signatureID"] = $_GET["ProcessID"];
$GLOBALS["signerID"] = $_GET["SignerID"];
sendMailAgain();
}
break;
case 'cancelProcess':
if(authSMO($token, $authClientId)){
$GLOBALS["signatureID"] = $_GET["ProcessID"];
$objectTypeID = $_GET["ObjectTypeID"];
$entityTypeID = $_GET["EntityTypeID"];
$entityID = $_GET["EntityID"];
$GLOBALS["winLangID"] = $_GET["WinLangID"];
$status = 1;
YouSignCancelProcess();
projectComment($objectTypeID, $entityTypeID, $entityID, $status);
cancelProcess($objectTypeID, $entityTypeID, $entityID);
}
break;
case 'getInfosProcess':
if(authSMO($token, $authClientId)){
$GLOBALS["signatureID"] = $_GET["ProcessID"];
getInfosProcess();
}
break;
case 'openProcess':
if(authSMO($token, $authClientId)){
$GLOBALS["signatureID"] = $_GET["ProcessID"];
$GLOBALS["signerID"] = $_GET["SignerID"];
openProcess();
}
break;
case 'docIsSigned':
if(authSMO($token, $authClientId)){
$status = 2;
$GLOBALS["signatureID"] = $_GET["ProcessID"];
$objectTypeID = $_GET["ObjectTypeID"];
$entityTypeID = $_GET["EntityTypeID"];
$entityID = $_GET["EntityID"];
$storeID = $_GET["StoreID"];
$GLOBALS["winLangID"] = $_GET["WinLangID"];
projectComment($objectTypeID, $entityTypeID, $entityID, $status);
signedDocumentYouSign($objectTypeID, $entityTypeID, $entityID);
}
break;
}
}
?>
HTML Sample
<!DOCTYPE HTML>
<html lang="">
<head>
<title>Electronic Signature</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://kit.fontawesome.com/FA_API_KEY_.js" crossorigin="anonymous"></script>
<script>
trad = {};
var signerID = '';
function GetUrlParameter(param){
var params = window.location.search.substring(1).split('&');
for(var i = 0; i < params.length; i++){
var paramName = params[i].split('=');
if(paramName[0] == param){
return paramName[1];
}
}
}
GetInfosProcess = function(){
if(document.getElementById('Mask')){
document.getElementById('Mask').classList.remove("noDisplay");
}
var params = new URLSearchParams();
axios.post('fenetre.php?action=getInfosProcess&ProcessID='+processID, params).then(function(resp) {
switch(resp.data.status){
case 'ongoing':
document.getElementById('Status').innerHTML = trad.encours;
break;
case 'done':
document.getElementById('Status').innerHTML = trad.signe;
break;
}
document.getElementById('CreatedOn').innerHTML = new Date(resp.data.created_at).toLocaleDateString('fr');
document.getElementById('ExpirationDate').innerHTML = new Date(resp.data.expiration_date).toLocaleDateString('fr');
if(resp.data.signers){
signerID = resp.data.signers[0].id;
}
if(resp.data.status == "done"){
document.getElementById('Action').classList.add("noDisplay");
DocIsSigned();
} else {
document.getElementById('Action').classList.remove("noDisplay");
}
if(document.getElementById('Mask')){
document.getElementById('Mask').classList.add("noDisplay");
}
document.getElementById("iframeProcedure").disabled = true;
document.getElementById("mailProcedure").disabled = true;
document.getElementById('OpenProcess').setAttribute('title', trad.consulterDemande);
document.getElementById('SendMailAgain').setAttribute('title', trad.renvoieEmail);
document.getElementById('CancelProcess').setAttribute('title', trad.annulationDemande);
document.getElementById('GetInfosProcess').setAttribute('title', trad.actualisationDemande);
});
}
CreateMailProcedure = function(){
parent.postMessage({MessageType:'lockButton'}, '*');
document.getElementById('Mask').classList.remove("noDisplay");
var params = new URLSearchParams();
axios.post('fenetre.php?action=createProcedure&ObjectTypeID='+objectTypeID+'&EntityTypeID=' + entityTypeID + '&EntityID=' + entityID + '&Localisation='+localisation+'&WinLangID='+winLangID, params).then(function(resp) {
parent.postMessage({MessageType:'unlockButton'}, '*');
if(resp.data.id){
processID = resp.data.id;
GetInfosProcess();
} else {
if(resp.data == "PHONEFAILURE") alert(trad.problemephone);
else if(resp.data == "MAILFAILURE") alert(trad.problememail);
else if(resp.data == "LASTNAMEFAILURE") alert(trad.problemelastname);
else if(resp.data == "FIRSTNAMEFAILURE") alert(trad.problemefirstname);
else alert(trad.alertCreationDemande);
}
document.getElementById('Mask').classList.add("noDisplay");
});
}
CreateIframeProcedure = function(){
parent.postMessage({MessageType:'lockButton'}, '*');
document.getElementById('Mask').classList.remove("noDisplay");
var params = new URLSearchParams();
axios.post('fenetre.php?action=iframeProcedure&ObjectTypeID='+objectTypeID+'&EntityTypeID=' + entityTypeID + '&EntityID=' + entityID + '&Localisation='+localisation+'&WinLangID='+winLangID, params).then(function(resp) {
parent.postMessage({MessageType:'unlockButton'}, '*');
if(resp.data.id && resp.data.signers[0].signature_link != null){
setTimeout(function(){
window.open( resp.data.signers[0].signature_link,'_blank');
}, 1000);
processID = resp.data.id;
GetInfosProcess();
} else {
if(resp.data == "PHONEFAILURE") alert(trad.problemephone);
else if(resp.data == "MAILFAILURE") alert(trad.problememail);
else if(resp.data == "LASTNAMEFAILURE") alert(trad.problemelastname);
else if(resp.data == "FIRSTNAMEFAILURE") alert(trad.problemefirstname);
else alert(trad.alertCreationDemande);
}
document.getElementById('Mask').classList.add("noDisplay");
});
}
SendMailAgain = function(){
document.getElementById('Mask').classList.remove("noDisplay");
var params = new URLSearchParams();
axios.post('fenetre.php?action=sendMailAgain&ProcessID='+processID + '&SignerID='+signerID, params).then(function(resp) {
document.getElementById('Mask').classList.add("noDisplay");
});
}
CancelProcess= function(){
document.getElementById('Mask').classList.remove("noDisplay");
var params = new URLSearchParams();
axios.post('fenetre.php?action=cancelProcess&ProcessID='+processID + '&ObjectTypeID='+objectTypeID+'&EntityTypeID=' + entityTypeID + '&EntityID=' + entityID + '&WinLangID='+winLangID, params)
.then(function(resp) {
document.getElementById('Status').innerHTML = "";
document.getElementById('CreatedOn').innerHTML = "";
document.getElementById('ExpirationDate').innerHTML = "";
document.getElementById('SignedOn').innerHTML = "";
processID = "";
signerID = "";
document.getElementById("iframeProcedure").disabled = false;
document.getElementById("mailProcedure").disabled = false;
document.getElementById('Action').classList.add("noDisplay");
document.getElementById('Mask').classList.add("noDisplay");
}
);
}
OpenProcess = function(){
document.getElementById('Mask').classList.remove("noDisplay");
var params = new URLSearchParams();
axios.post('fenetre.php?action=openProcess&ProcessID='+processID+ '&SignerID='+signerID, params).then(function(resp) {
if(resp.data.signature_link && resp.data.signature_link != null){
window.open( resp.data.signature_link, '_blank');
} else {
alert(trad.alertOuvertureDemande);
}
document.getElementById('Mask').classList.add("noDisplay");
});
}
DocIsSigned = function(){
var params = new URLSearchParams();
axios.post('fenetre.php?action=docIsSigned&ProcessID='+processID + '&ObjectTypeID='+objectTypeID+'&EntityTypeID=' + entityTypeID + '&EntityID=' + entityID + '&StoreID=' + storeID+'&WinLangID='+winLangID, params)
.then(function(resp) {
document.getElementById('Mask').classList.add("noDisplay");
}
);
}
OpenWindow = function(){
if(processID && processID.length > 0){
GetInfosProcess();
} else {
document.getElementById("iframeProcedure").disabled = false;
document.getElementById("mailProcedure").disabled = false;
}
}
TradWrite = function(param){
document.write(trad[param]);
}
var entityTypeID = GetUrlParameter('entityTypeID');
var entityID = GetUrlParameter('entityID');
var storeID = GetUrlParameter('storeID');
var objectTypeID = GetUrlParameter('objectTypeID');
var processID = GetUrlParameter('processID');
var localisation = GetUrlParameter('localisation');
var winLangID = GetUrlParameter('winLangID');
parent.postMessage({MessageType:'labelButtonOK', LabelButton:'CUSTOMIZABLE', IconButton: 'fa fa-ICON'}, '*');
parent.postMessage({MessageType:'screenSize', Width: "750px", Height: "90%"}, '*');
parent.postMessage({MessageType:'setTitle', Title: "CUSTOMIZABLE"}, '*');
switch(localisation){
case 'fr':
trad = {
"signatureDirect": "Signature Directe",
"signatureDirectText": "Signature directe avec le client: envoi d'un sms de confirmation <br> au numéro de mobile renseigné",
"signatureMail": "Demande par mail",
"signatureMailText": "Envoi par email de la demande de signature",
"dateDemande": "Date de la demande",
"statutDemande": "Statut",
"expirationDemande": "Expiration de la demande",
"actions": "Actions",
"consulterDemande": "Consulter la demande",
"renvoieEmail": "Renvoyer un email",
"annulationDemande": "Annulation de la procédure",
"actualisationDemande": "Rafraîchir le statut",
"alertCreationDemande": "Nous n'avons pas pu créer la procédure",
"alertOuvertureDemande": "Nous n'avons pas pu ouvrir la procédure",
"encours": "En cours",
"signe": "Signé",
"problemephone": "Numéro de téléphone mobile du client non renseigné ou incorrect (format +33 exigé).",
"problememail": "Email du client non renseigné ou incorrect.",
"problemelastname": "Nom du client ou de la société non renseigné.",
"problemefirstname": "Prénom du client ou titre la société non renseigné."
}
break;
case 'sp':
trad = {
"signatureDirect": "Firma directa",
"signatureDirectText": "Firma directa con el cliente: enviar un sms de <br> confirmación al número de teléfono móvil introducido",
"signatureMail": "Nueva solicitud de firma",
"signatureMailText": "Envío por email de la solicitud de firma",
"dateDemande": "Fecha de la solicitud",
"statutDemande": "Estado",
"expirationDemande": "Expiración de la solicitud",
"actions": "Acciones",
"consulterDemande": "Consultar la solicitud",
"renvoieEmail": "Reenviar un email",
"annulationDemande": "Anulación del solicitud",
"actualisationDemande": "Actualizar estado",
"alertCreationDemande": "No hemos podido crear el procedimiento",
"alertOuvertureDemande": "No pudimos iniciar el procedimiento",
"encours": "En curso",
"signe": "Firmado",
"problemephone": "Número de teléfono móvil del cliente no introducido o incorrecto (formato +34 obligatorio).",
"problememail": "Correo electrónico del cliente no introducido o incorrecto.",
"problemelastname": "No se especifica el nombre del cliente o de la empresa.",
"problemefirstname": "No se ha introducido el nombre del cliente o el título de la empresa."
}
break;
case 'en':
default:
trad = {
"signatureDirect": "Live signature",
"signatureDirectText": "Signed directly with client: confirm via SMS using <br> mobile number on file",
"signatureMail": "New signature request",
"signatureMailText": "Send signature request via e-mail",
"dateDemande": "Request date",
"statutDemande": "Status",
"expirationDemande": "Request expires",
"actions": "Actions",
"consulterDemande": "Consult the request",
"renvoieEmail": "Send e-mail",
"annulationDemande": "Cancellation of the procedure",
"actualisationDemande": "Refresh status",
"alertCreationDemande": "We were unable to create the request",
"alertOuvertureDemande": "We were unable to initiate the request",
"encours": "Current",
"signe": "Signed",
"problemephone": "Customer's mobile phone number not specified or incorrect (+44 format required).",
"problememail": "Customer email not specified or incorrect.",
"problemelastname": "Customer or company name not specified.",
"problemefirstname": "First name of customer or company title not specified."
}
break;
}
</script>
<style>
.frame {
height: 100%;
width: 100%;
font-weight: 400;
font-family: helvetica, arial, verdana, sans-serif;
}
.buttonExt{
width:170px;
}
.inline{
display: inline-block;
}
.buttonText{
font-size:12px;
}
.inlineParent{
white-space: nowrap;
}
table, th, td{
font-size: 13px;
}
th{
border-bottom: 1px solid;
font-weight: unset !important;
}
.noDisplay{
display: none;
}
button{
display: inline-flex;
flex-direction: column;
align-items: stretch;
position: relative;
outline: 0;
vertical-align: middle;
padding: 0;
font-size: 14px;
line-height: 1.715em;
text-decoration: none;
color: inherit;
background: transparent;
font-weight: 500;
text-align: center;
width: auto;
border-color: #ef8000;
border-width: 1px;
border-style: solid;
background: #fff;
color: #ef8000;
padding: 4px 4px !important;
min-height: unset !important;
height: unset !important;
cursor: pointer;
}
button:hover {
background-color: #ef8000;
color: white;
}
button:disabled{
color: #3b3c3e !important;
background-color: #cdcccd !important;
opacity: .6 !important;
border: 0;
}
::-webkit-scrollbar {
width: 16px;
height: 16px
}
::-webkit-scrollbar-thumb {
background-color: #d4d4d4;
border: solid transparent;
border-width: 5px;
border-radius: 10px;
background-clip: content-box
}
::-webkit-scrollbar-thumb:hover {
background-color: #e1e1e1
}
td{
text-align: center;
}
.mt{
margin-top: 5px;
}
.ml{
margin-left: 5px;
}
.mr{
margin-right: 5px;
}
body{
margin: 0px;
font-family: Arial;
}
.mz{
margin: 0;
vertical-align: middle;
}
.noBorder{
border: none;
}
.mask{
position:absolute;
left:0px;
top:0px;
font-family:verdana;
font-weight:bold;
z-index:100;
right: 0;
bottom: 0;
background: rgba(200, 200, 200, 0.6);
}
</style>
</head>
<body onload="OpenWindow()">
<div style="padding:5px;">
<div class="inlineParent" id="buttonCreateIframe">
<button id="iframeProcedure" disabled class="inline buttonExt" onclick="CreateIframeProcedure()"><script>TradWrite("signatureDirect")</script></button>
<p class="inline buttonText mz" ><script>TradWrite("signatureDirectText")</script></p>
</div>
<div class="inlineParent" id="buttonCreateMail" style="margin-top: 5px;">
<button id="mailProcedure" disabled class="inline buttonExt" onclick="CreateMailProcedure()" type="button"><script>TradWrite("signatureMail")</script></button>
<p class="inline buttonText"><script>TradWrite("signatureMailText")</script></p>
</div>
<table id="infosProcess" style="margin-top: 10px;">
<thead>
<tr>
<th><script>TradWrite("dateDemande")</script></th>
<th style="width:150px;"><script>TradWrite("statutDemande")</script></th>
<th><script>TradWrite("expirationDemande")</script></th>
<th style="width:150px;"><script>TradWrite("actions")</script></th>
</tr>
</thead>
<tbody>
<tr>
<td id="CreatedOn"></td>
<td><span id="Status"></span><span id="SignedOn"></span></td>
<td id="ExpirationDate"></td>
<td id="Action" class="noDisplay">
<button class="inline noBorder" onclick="OpenProcess()" id="OpenProcess" type="button"><i class="fa-solid fa-pen"></i></button><!--Réouvrir-->
<button class="inline" onclick="SendMailAgain()" id="SendMailAgain" type="button"><i class="fa-solid fa-envelope mr"></i><i class="fa-solid fa-share"></i></button> <!-- Renvoi de l'email-->
<button class="inline noBorder" onclick="CancelProcess()" id="CancelProcess" type="button"><i class="fa-solid fa-ban"></i></button><!--Annulation de la procédure-->
<button class="inline noBorder" onclick="GetInfosProcess()" id="GetInfosProcess" type="button"><i class="fa-solid fa-arrow-rotate-right"></i></button><!--Annulation de la procédure-->
</td>
</tr>
</tbody>
</table>
</div>
<div id="Mask" class="mask noDisplay"></div>
</body>
</html>