To send data from your application use the following code to encrypt data before sending to the external api which you can checkout on swagger. Following vaiables must be present in encryptedData.
const data = JSON.stringify({"first_name": "John", "last_name": "Doe", "email": "a@a.com", "unique_identifier": "xxxxxxx-xxxxxxxxx-xxxx"})
function encrptData(data: string){
const algorithm = "aes-256-gcm";
const publicKey = "2eaf548c6022de5a3293fbfdd595afad04f750d9cc861d9c723229bb34fa679f".substring(0, 24);
const privateKey = Buffer.from("2eaf548c6022de5a3293fbfdd595afad04f750d9cc861d9c723229bb34fa679f", 'hex');
const cipher = crypto.createCipheriv(algorithm, privateKey, publicKey);
let enc = cipher.update(data, 'utf8', 'hex');
enc += cipher.final('hex');
return enc;
}
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class Main {
public static void main(String[] args) throws Exception {
String encryptedData = encryptData("test", "testing", "admin@user.com", "1");
System.out.println(encryptedData);
}
public static String encryptData(String firstName, String lastName, String email, String Id) throws Exception {
String data = "{\"first_name\":\""+firstName+"\",\"last_name\":\""+lastName+"\",\"email\":\""+email+"\",\"unique_identifier\":\""+Id+"\"}";
String algorithm = "AES/GCM/NoPadding";
String publicKey = "2eaf548c6022de5a3293fbfdd595afad04f750d9cc861d9c723229bb34fa679f".substring(0, 24);
String privateKeyHex = "2eaf548c6022de5a3293fbfdd595afad04f750d9cc861d9c723229bb34fa679f";
byte[] publicKeyBytes = publicKey.getBytes(StandardCharsets.UTF_8);
byte[] privateKeyBytes = hexStringToByteArray(privateKeyHex);
SecretKey secretKey = new SecretKeySpec(privateKeyBytes, "AES");
Cipher cipher = Cipher.getInstance(algorithm);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, publicKeyBytes);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmParameterSpec);
byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return bytesToHex(encryptedBytes).substring(0, 184);
}
public static byte[] hexStringToByteArray(String hexString) {
int length = hexString.length();
byte[] byteArray = new byte[length / 2];
for (int i = 0; i < length; i += 2) {
byteArray[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) +
Character.digit(hexString.charAt(i + 1), 16));
}
return byteArray;
}
public static String bytesToHex(byte[] bytes) {
StringBuilder result = new StringBuilder();
for (byte aByte : bytes) {
result.append(String.format("%02x", aByte));
}
return result.toString();
}
}
For callback url you can following parameters in the admin panel.
Callback URL should look something like the following
http://example.com/verification-completed?user_id=[user_id]&status=[status]&email=[email]
For webhooks you first need to add url where you want the payload inside the webhook section of integeration in admin panel. There are three types of webhooks
Following is the payload you will get with all the webhook types. "uniqueIdentifier" will have the "unique_identifier" sent during the creation of verificaiton
id: string, //verification id to be saved
integrationId: string,
status: VerificationStatusEnum,
uniqueIdentifier: string,
documentType?: VerificationDocumentEnum | null,
requestUrl: string,
hasError?: boolean,
phoneNumberVerified: boolean,
verifiedPhoneNumber: string,
documentBack: {url: string},
documentFront: {url: string},
verificationDetails: {
firstName: string,
lastName: string,
dateOfBirth: string | null,
gender: string | null,
idNumber: string | null,
nationality: string | null,
placeOfBirth: string | null,
name: string | null,
surname: string | null,
expiration_date: string | null,
address: string | null,
country: string | null,
}
reason: string | null,
Not Started,
Started,
Submitted,
Expired,
Abandoned,
Declined,
Approved,
Under Review,
Passport,
License,
Address Permit,
Proof Address,
Following is the example for how to implement the webhooks
import {
Body,
Controller,
Inject,
Post,
Logger,
HttpCode,
HttpStatus,
} from '@nestjs/common';
import { ClientRMQ } from '@nestjs/microservices';
import { MESSAGE_PATTERNS, SERVICES } from '../constants';
import { firstValueFrom } from 'rxjs';
import { ApiExcludeController } from '@nestjs/swagger';
// Enums
enum VerificationStatus {
Not_Started = "Not Started",
Started = "Started",
Submitted = "Submitted",
Expired = "Expired",
Abandoned = "Abandoned",
Declined = "Declined",
Approved = "Approved",
UnderReview = "Under Review",
}
export enum VerificationDocument {
Passport = "Passport",
License = "License",
Address_Permit = "Address Permit",
Proof_Address = "Proof Address",
}
// Webhook Request Format
export class WebhookRequestDto {
id: string, //verification id to be saved
integrationId: string,
status: VerificationStatusEnum,
uniqueIdentifier: string,
documentType?: VerificationDocumentEnum | null,
requestUrl: string,
hasError?: boolean,
phoneNumberVerified: boolean,
verifiedPhoneNumber: string,
documentBack: {url: string},
documentFront: {url: string},
verificationDetails: {
firstName: string,
lastName: string,
dateOfBirth: string | null,
gender: string | null,
idNumber: string | null,
nationality: string | null,
placeOfBirth: string | null,
name: string | null,
surname: string | null,
expiration_date: string | null,
address: string | null,
country: string | null,
}
reason: string | null,
}
const {
ADMIN_USER: { ADMIN_CONFIRM_SIGNUP },
} = MESSAGE_PATTERNS.USER_ACCOUNT;
const {
USER: { UPDATE_USER },
} = MESSAGE_PATTERNS.USER_PROFILE;
@ApiExcludeController()
@Controller('webhooks')
export class WebhookController {
private readonly logger = new Logger(WebhookController.name);
constructor(
@Inject(SERVICES.USER_ACCOUNT) private authClient: ClientRMQ,
@Inject(SERVICES.USER_PROFILE) private userClient: ClientRMQ,
) {}
@Post('status')
@HttpCode(HttpStatus.OK)
async status(@Body() dto: WebhookRequestDto) {
// Signed up for user as documents are submitted which means that it was a verified email
if (dto.status === VerificationStatus.Submitted) {
await firstValueFrom(
this.authClient.send(ADMIN_CONFIRM_SIGNUP, {
userId: dto.uniqueIdentifier,
}),
);
}
await firstValueFrom(
this.userClient.send(UPDATE_USER, {
userId: dto.uniqueIdentifier,
idVerificationStatus: dto.status,
// Email verified and active
...(dto.status === VerificationStatus.Submitted && {
emailVerified: new Date(),
isActive: true,
}),
}),
);
return {
data: null,
message: 'Integration client has received the status.',
errors: null,
};
}
@Post('decision')
@HttpCode(HttpStatus.OK)
async decision(@Body() dto: WebhookRequestDto) {
// User Syncing
await firstValueFrom(
this.userClient.send(UPDATE_USER, {
userId: dto.uniqueIdentifier,
idVerified: dto.status === VerificationStatus.Approved,
idVerificationStatus: dto.status,
}),
);
return {
data: null,
message: `Integration client has received the decision.`,
errors: null,
};
}
@Post('proof-of-address')
@HttpCode(HttpStatus.OK)
async proofOfAddress(@Body() dto: WebhookRequestDto) {
// Scenario goes here
return {
data: null,
message: `Integration client has received the decision.`,
errors: null,
};
}
}