diff --git a/cp/app/Controllers/ContactsController.php b/cp/app/Controllers/ContactsController.php
index b778b71..82c158a 100644
--- a/cp/app/Controllers/ContactsController.php
+++ b/cp/app/Controllers/ContactsController.php
@@ -3,6 +3,7 @@
namespace App\Controllers;
use App\Models\Contact;
+use App\Lib\Mail;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Container\ContainerInterface;
@@ -1134,36 +1135,93 @@ class ContactsController extends Controller
} else {
$clid = $contact['clid'];
}
-
+
if ($contact) {
- try {
- $db->beginTransaction();
- $currentDateTime = new \DateTime();
- $stamp = $currentDateTime->format('Y-m-d H:i:s.v');
- $db->update(
- 'contact',
- [
- 'validation' => $data['verify'],
- 'validation_stamp' => $stamp,
- 'validation_log' => json_encode($data['v_log']),
- 'upid' => $clid,
- 'lastupdate' => $stamp
+ if (!empty(envi('SUMSUB_TOKEN')) && !empty(envi('SUMSUB_KEY'))) {
+ $level_name = 'idv-and-phone-verification';
+
+ // Build request body
+ $bodyArray = [
+ 'levelName' => $level_name,
+ 'userId' => $identifier,
+ 'applicantIdentifiers' => [
+ 'email' => $contact['email'],
+ 'phone' => $contact['voice']
],
- [
- 'identifier' => $identifier
+ 'ttlInSecs' => 1800
+ ];
+
+ $body = json_encode($bodyArray);
+ $path = '/resources/sdkIntegrations/levels/-/websdkLink';
+ $ts = time();
+ $signature = sign($ts, 'POST', $path, $body, envi('SUMSUB_KEY'));
+
+ // Guzzle client
+ $client = new \GuzzleHttp\Client([
+ 'base_uri' => 'https://api.sumsub.com',
+ 'headers' => [
+ 'X-App-Token' => envi('SUMSUB_TOKEN'),
+ 'X-App-Access-Ts' => $ts,
+ 'X-App-Access-Sig' => $signature,
+ 'Content-Type' => 'application/json',
]
- );
- $db->commit();
- } catch (Exception $e) {
- $db->rollBack();
- $this->container->get('flash')->addMessage('error', 'Database failure during update: ' . $e->getMessage());
+ ]);
+
+ // Send request
+ try {
+ $response = $client->post($path, ['body' => $body]);
+ $data = json_decode($response->getBody(), true);
+ $link = $data['url'];
+
+ $currentDateTime = new \DateTime();
+ $stamp = $currentDateTime->format('Y-m-d H:i:s.v');
+ $email = $db->selectValue('SELECT email FROM users WHERE id = ?', [$_SESSION['auth_user_id']]);
+ $registry = $db->selectValue('SELECT value FROM settings WHERE name = ?', ['company_name']);
+ $message = file_get_contents(__DIR__.'/../../resources/views/mail/validation.html');
+ $placeholders = ['{registry}', '{link}', '{app_name}', '{app_url}', '{identifier}'];
+ $replacements = [$registry, $link, envi('APP_NAME'), envi('APP_URL'), $contact['identifier']];
+ $message = str_replace($placeholders, $replacements, $message);
+ $mailsubject = '[' . envi('APP_NAME') . '] Contact Verification Required';
+ $from = ['email'=>envi('MAIL_FROM_ADDRESS'), 'name'=>envi('MAIL_FROM_NAME')];
+ $to = ['email'=>$contact['email'], 'name'=>''];
+ // send message
+ Mail::send($mailsubject, $message, $from, $to);
+
+ $this->container->get('flash')->addMessage('info', 'Contact validation process initiated with SumSub on ' . $stamp);
+ return $response->withHeader('Location', '/contact/update/'.$identifier)->withStatus(302);
+ } catch (\GuzzleHttp\Exception\ClientException $e) {
+ $this->container->get('flash')->addMessage('error', 'Contact validation error: ' . $e->getMessage());
+ return $response->withHeader('Location', '/contact/update/'.$identifier)->withStatus(302);
+ }
+ } else {
+ try {
+ $db->beginTransaction();
+ $currentDateTime = new \DateTime();
+ $stamp = $currentDateTime->format('Y-m-d H:i:s.v');
+ $db->update(
+ 'contact',
+ [
+ 'validation' => $data['verify'],
+ 'validation_stamp' => $stamp,
+ 'validation_log' => json_encode($data['v_log']),
+ 'upid' => $clid,
+ 'lastupdate' => $stamp
+ ],
+ [
+ 'identifier' => $identifier
+ ]
+ );
+ $db->commit();
+ } catch (Exception $e) {
+ $db->rollBack();
+ $this->container->get('flash')->addMessage('error', 'Database failure during update: ' . $e->getMessage());
+ return $response->withHeader('Location', '/contact/update/'.$identifier)->withStatus(302);
+ }
+
+ unset($_SESSION['contacts_to_validate']);
+ $this->container->get('flash')->addMessage('success', 'Contact ' . $identifier . ' has been validated successfully on ' . $stamp);
return $response->withHeader('Location', '/contact/update/'.$identifier)->withStatus(302);
}
-
- unset($_SESSION['contacts_to_validate']);
- $this->container->get('flash')->addMessage('success', 'Contact ' . $identifier . ' has been validated successfully on ' . $stamp);
- return $response->withHeader('Location', '/contact/update/'.$identifier)->withStatus(302);
-
} else {
// Contact does not exist, redirect to the contacts view
return $response->withHeader('Location', '/contacts')->withStatus(302);
@@ -1628,6 +1686,64 @@ class ContactsController extends Controller
//}
- }
+ }
+
+ public function webhookSumsub(Request $request, Response $response)
+ {
+ $body = $request->getBody()->getContents();
+ $data = json_decode($body, true);
+ $db = $this->container->get('db');
+
+ // Validate input
+ if (!isset($data['externalUserId']) || !isset($data['type'])) {
+ $response->getBody()->write('Missing required fields');
+ return $response->withStatus(400);
+ }
+
+ $identifier = $data['externalUserId'];
+ $type = $data['type'];
+
+ // Only process applicantReviewed type
+ if ($type === 'applicantReviewed') {
+ $answer = $data['reviewResult']['reviewAnswer'] ?? null;
+ switch ($answer) {
+ case 'GREEN':
+ $verify = '4'; // verified
+ break;
+ case 'RED':
+ $verify = '0'; // failed
+ break;
+ default:
+ // Ignore anything else
+ $response->getBody()->write('Ignored (unhandled reviewAnswer)');
+ return $response->withStatus(202);
+ }
+ $v_log = $data; // store full webhook for audit
+ $clid = $data['applicantId'] ?? null;
+
+ $currentDateTime = new \DateTime();
+ $stamp = $currentDateTime->format('Y-m-d H:i:s.v');
+
+ $db->update(
+ 'contact',
+ [
+ 'validation' => $verify,
+ 'validation_stamp' => $stamp,
+ 'validation_log' => json_encode($v_log),
+ //'upid' => $clid,
+ 'lastupdate' => $stamp
+ ],
+ [
+ 'identifier' => $identifier
+ ]
+ );
+
+ $response->getBody()->write('OK');
+ return $response->withStatus(200);
+ }
+
+ $response->getBody()->write('Ignored');
+ return $response->withStatus(202);
+ }
}
\ No newline at end of file
diff --git a/cp/bootstrap/helper.php b/cp/bootstrap/helper.php
index 0a6f77b..ad28100 100644
--- a/cp/bootstrap/helper.php
+++ b/cp/bootstrap/helper.php
@@ -1148,4 +1148,10 @@ function isValidHostname($hostname) {
}
return true;
+}
+
+// HMAC Signature generator
+function sign($ts, $method, $path, $body, $secret_key) {
+ $stringToSign = $ts . strtoupper($method) . $path . $body;
+ return hash_hmac('sha256', $stringToSign, $secret_key);
}
\ No newline at end of file
diff --git a/cp/env-sample b/cp/env-sample
index 97c3ad4..c8f90fd 100644
--- a/cp/env-sample
+++ b/cp/env-sample
@@ -44,4 +44,7 @@ NOW_API_KEY='now-api-key'
NICKY_API_KEY='nicky-api-key'
+SUMSUB_TOKEN=
+SUMSUB_KEY=
+
TEST_TLDS=.test,.com.test
\ No newline at end of file
diff --git a/cp/resources/views/mail/validation.html b/cp/resources/views/mail/validation.html
new file mode 100644
index 0000000..ef55993
--- /dev/null
+++ b/cp/resources/views/mail/validation.html
@@ -0,0 +1,526 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {registry}
+
+ |
+
+
+
+
+
+
+
+
+
+ Contact Verification Required
+ Dear Contact {identifier},
+ We would like to inform you that the registry {registry} has initiated a contact verification process for your domain registration data.
+ To ensure compliance with our domain policies, please complete the verification by following the link below:
+
+
+ This verification is required to confirm the accuracy of your contact details associated with one or more domain names.
+ If the verification is not completed within 15 days, it may result in temporary suspension or limitations on your domain services. We kindly urge you to complete the verification as soon as possible to avoid any disruptions.
+ Thank you for choosing {registry} for your needs. We are here to help!
+ Kind regards,
+ {registry} Support Team
+
+ |
+
+
+ |
+
+
+
+
+ |
+
+
+ |
+
+
+
+
\ No newline at end of file
diff --git a/cp/routes/web.php b/cp/routes/web.php
index 61cbe44..b0a09a8 100644
--- a/cp/routes/web.php
+++ b/cp/routes/web.php
@@ -38,6 +38,7 @@ $app->group('', function ($route) {
$route->get('/update-password', PasswordController::class.':createUpdatePassword')->setName('update.password');
$route->post('/update-password', PasswordController::class.':updatePassword');
$route->post('/webhook/adyen', FinancialsController::class .':webhookAdyen')->setName('webhookAdyen');
+ $route->post('/webhook/sumsub', ContactsController::class .':webhookSumsub')->setName('webhookSumsub');
})->add(new GuestMiddleware($container));
$app->group('', function ($route) {