<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!------ Include the above in your HEAD tag ---------->
<div class="container">
<div class="row">
<h2>Add sumsub in node js </h2>
</div>
</div>
/*create token gen file*/
const axios = require('axios');
const crypto = require('crypto');
// const fs = require('fs');
const FormData = require('form-data');
import fs from 'fs'
// These parameters should be used for all requests
const SUMSUB_APP_TOKEN = 'prd:IK6KSFu9pbxh1i3YuBN7Z8oT.9ldqtyMnRNJ9yPAT2bSBaolhThFdLd3f'; // Example: sbx:uY0CgwELmgUAEyl4hNWxLngb.0WSeQeiYny4WEqmAALEAiK2qTC96fBad - Please don't forget to change when switching to production
const SUMSUB_SECRET_KEY = 'G5EKB0oPCxrlcR4pGJ25hcvcwQYfJVVB'; // Example: Hej2ch71kG2kTd1iIUDZFNsO5C1lh5Gq - Please don't forget to change when switching to production
const SUMSUB_BASE_URL = 'https://api.sumsub.com';
var config: any = {};
config.baseURL = SUMSUB_BASE_URL;
axios.interceptors.request.use(createSignature, function (error: any) {
return Promise.reject(error);
})
// This function creates signature for the request as described here: https://developers.sumsub.com/api-reference/#app-tokens
function createSignature(config: any) {
console.log('Creating a signature for the request...');
var ts = Math.floor(Date.now() / 1000);
const signature = crypto.createHmac('sha256', SUMSUB_SECRET_KEY);
signature.update(ts + config.method.toUpperCase() + config.url);
if (config.data instanceof FormData) {
signature.update(config.data.getBuffer());
} else if (config.data) {
signature.update(config.data);
}
config.headers['X-App-Access-Ts'] = ts;
config.headers['X-App-Access-Sig'] = signature.digest('hex');
return config;
}
// These functions configure requests for specified method
// https://developers.sumsub.com/api-reference/#creating-an-applicant
function createApplicant(externalUserId: any, levelName: any) {
console.log("Creating an applicant...");
var method = 'post';
var url = '/resources/applicants?levelName=' + levelName;
var ts = Math.floor(Date.now() / 1000);
var body = {
externalUserId: externalUserId
};
var headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-App-Token': SUMSUB_APP_TOKEN
};
config.method = method;
config.url = url;
config.headers = headers;
config.data = JSON.stringify(body);
return config;
}
// https://developers.sumsub.com/api-reference/#adding-an-id-document
function addDocument(applicantId: any) {
console.log("Adding document to the applicant...");
var method = 'post';
var url = `/resources/applicants/${applicantId}/info/idDoc`;
var filePath = 'resources/sumsub-logo.png';
var metadata = {
idDocType: 'PASSPORT',
country: 'GBR'
};
var form = new FormData();
form.append('metadata', JSON.stringify(metadata));
// var content = fs.readFileSync(filePath);
// form.append('content', content, filePath);
/*
In case you'd like to upload images in base64 encoded string format:
var content = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAMAAABlApw1AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAABCUExURUxpcSMudAGjmiIwdiEtdSIwdCMxdSMwdSMwdQGmlyMvdSFPfQOnmCMvdSMwdSMwdQGjmiMwdQGjmgGjmiMwdQGjmlncPbUAAAAUdFJOUwAw5lQRH0PM8CGpBhC81eBrftK5jzDo3gAAAAlwSFlzAAABwAAAAcABl8K+3QAAAuRJREFUeNrtm9typCAURXFEdMRLm6T//1fnId4qfUKDomLNWo8pwLOq7WwP0koBAAAAAAAAAAD8xzz+BPJIq377DMbeXaBBAAEEEIhHc3eBR7hAEkn2+Bhz9bmBcerH41Z3flqZ3MQRaBBAAAEEEEAAAQQQQAABBO7QCV/YHc/978IzEi8LH9EnN89Tid8n/z1XoEEAAQQQuPTf6AHp/Hlm/Z8BhQ1d/4PSyDfRiYgFmLb8yaBU8fVKqZKkFUotVCb8tU9ToBNKzRBAAAEEEhQY0hQY3gkMWfGNSpSxvGyQBXp1G3pRoLuPQIcAAgggcCnlSsCWieevM5M7q5Qd802vBuSv3aeT1vjNXY8zodfIVwVmI7/sAeuvUFa3X+UaV0lp6ov2f+YIXnspzLoHeoqKFAgggAACCCQhUJvvGDRtHIF2Wq8+SWDO7yyOwFyYrU4RqIRHjl0C2vO2QgABBBDYL2CFYm10AXEjW+qq5n66XjpFt8DSF86p1Xqmc8imgzbf5J1bwI7jjJ074XdPCN3U4S5z3QJdPo7TagOZW0B5Zqi0itSPV+IbgF078p4C4dsAYote7bvzEUAAAQR2CvTRBaRMroVhebhALixT78tfVyavsK870Z3QMUuY9ZmldiQXkn1BqwMR89d5Re2byeegPb8hK/ZtmyOAAAII7BWokheo3VPqVAXm/H2XfUImJyFgd8xNQaAKn4wAAggggICJI5CfXnc9drNVHIGpO67NOfVLnfA+gZO74w3PoGk9WCOAAAIIXNsdb+iEJdojBUw9UrgF2vx1x9o7D6d30W+642KqJSCn9fzxlid0s2/Wm/2qLWen+9jPoO5vg1Rit+uwBwIIIIDAFQK293snHF3A+e64D0hLnY/YOZMXquMEqterGTvVsik1te9vkuIIxO+Tfc8vHidwwGEPBBBAAIHzBKzzN6d1HIE21olpZyZLRNqLta5rXPfyAAAAAAAAAAAAAH7nHygtt70j9IRfAAAAV3pUWHRSYXcgcHJvZmlsZSB0eXBlIGlwdGMAAHic4/IMCHFWKCjKT8vMSeVSAAMjCy5jCxMjE0uTFAMTIESANMNkAyOzVCDL2NTIxMzEHMQHy4BIoEouAOoXEXTyQjWVAAAAAElFTkSuQmCC'
var ext = content.substring("data:image/".length, content.indexOf(";base64"));
var fileName = `image.${ext}`;
var base64Data = content.split(',')[1];
form.append('content', Buffer.from(base64Data, 'base64'), { fileName });
*/
var headers = {
'Accept': 'application/json',
'X-App-Token': SUMSUB_APP_TOKEN
};
config.method = method;
config.url = url;
config.headers = Object.assign(headers, form.getHeaders());
config.data = form;
return config;
}
// https://developers.sumsub.com/api-reference/#getting-applicant-status-sdk
function getApplicantStatus(applicantId: any) {
console.log("Getting the applicant status...");
var method = 'get';
var url = `/resources/applicants/${applicantId}/status`;
var headers = {
'Accept': 'application/json',
'X-App-Token': SUMSUB_APP_TOKEN
};
config.method = method;
config.url = url;
config.headers = headers;
config.data = null;
return config;
}
// https://developers.sumsub.com/api-reference/#access-tokens-for-sdks
function createAccessToken(externalUserId: any, levelName = 'basic-kyc-level', ttlInSecs = 31556952 *10) {
console.log("Creating an access token for initializng SDK...");
var method = 'post';
var url = `/resources/accessTokens?userId=${externalUserId}&ttlInSecs=${ttlInSecs}&levelName=${levelName}`;
var headers = {
'Accept': 'application/json',
'X-App-Token': SUMSUB_APP_TOKEN
};
config.method = method;
config.url = url;
config.headers = headers;
config.data = null;
return config;
}
// This section contains requests to server using configuration functions
// The description of the flow can be found here: https://developers.sumsub.com/api-flow/#api-integration-phases
// Such actions are presented below:
// 1) Creating an applicant
// 2) Adding a document to the applicant
// 3) Getting applicant status
// 4) Getting access tokens for SDKs
export default async function main() {
let externalUserId = "random-JSToken-" + Math.random().toString(36).substr(2, 9);
let levelName = 'basic-kyc-level';
console.log("External UserID: ", externalUserId);
let response = await axios(createApplicant(externalUserId, levelName))
.then(function (response: any) {
console.log("Response:\n", response.data);
return response;
})
.catch(function (error: any) {
console.log("Error:\n", error.response.data);
});
const applicantId = response.data.id;
console.log("ApplicantID: ", applicantId);
response = await axios(addDocument(applicantId))
.then(function (response: any) {
console.log("Response:\n", response.data);
return response;
})
.catch(function (error: any) {
console.log("Error:\n", error.response.data);
});
response = await axios(getApplicantStatus(applicantId))
.then(function (response: any) {
console.log("Response:\n", response.data);
return response;
})
.catch(function (error: any) {
console.log("Error:\n", error.response.data);
});
let resultResponse = await axios(createAccessToken(externalUserId, levelName))
.then(function (response: any) {
console.log("Result Response:\n", response.data);
return { ...response.data, applicantId };
})
.catch(function (error: any) {
console.log("Error:\n", error.response.data);
});
return resultResponse;
}
/**
* createLink for sumsub webhook
*/
@Post("/verify_kyc_response")
public async verify_kyc_response(@Body() request: any): Promise<IResponse> {
try {
let data = request;
let updateData: { [key: string]: any } = {};
let exists = await findOne(clientModel, { applicant_id: data.applicantId })
if (!exists) {
throw new Error('User not found!')
}
if (data.reviewStatus == 'pending' || data.reviewStatus == 'onHold') {
updateData['kyc_status'] = 'PENDING'
}
if (data.reviewStatus == 'completed' && data.reviewResult.reviewAnswer == 'RED') {
updateData['kyc_status'] = 'REJECTED'
}
if (data.reviewStatus == 'completed' && data.reviewResult.reviewAnswer == 'GREEN') {
updateData['kyc_status'] = 'COMPLETED'
}
updateData = await upsert(clientModel, updateData, exists._id)
console.log(data, ">>>>>>>>>>>>>")
return {
data: {},
error: "",
message: MESSAGES.INFO_FETCHED,
status: 200,
};
} catch (err: any) {
logger.error(`${this.req.ip} ${err.message}`);
return {
data: null,
error: err.message ? err.message : err,
message: "",
status: 400,
};
}
}
// route
router.post('/verify_kyc_response', async (req: Request | any, res: Response) => {
console.log(req.body, "?????????????????????????????????")
const controller = new ClientController(req, res)
const response = await controller.verify_kyc_response(req.body);
return responseWithStatus(res, response.status, response)
})