xrocket-pay-api-sdk
Version:
TypeScript SDK for xRocket Pay API
152 lines (151 loc) • 5.23 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebhookParseError = exports.WebhookSignatureError = void 0;
exports.verifyWebhookSignature = verifyWebhookSignature;
exports.parseWebhookPayload = parseWebhookPayload;
exports.verifyAndParseWebhook = verifyAndParseWebhook;
exports.isInvoicePaid = isInvoicePaid;
exports.extractPaymentInfo = extractPaymentInfo;
const crypto_1 = require("crypto");
const types_1 = require("./types");
/**
* Custom error types for webhook operations
*/
class WebhookSignatureError extends Error {
constructor(message) {
super(message);
this.name = 'WebhookSignatureError';
}
}
exports.WebhookSignatureError = WebhookSignatureError;
class WebhookParseError extends Error {
constructor(message) {
super(message);
this.name = 'WebhookParseError';
}
}
exports.WebhookParseError = WebhookParseError;
/**
* Verify webhook signature using HMAC-SHA-256
*
* @param body - Raw request body as string
* @param signature - The rocket-pay-signature header value
* @param secret - Your webhook secret key
* @returns true if signature is valid, false otherwise
*/
function verifyWebhookSignature(body, signature, token) {
if (!body || !signature || !token) {
return false;
}
try {
const secret = (0, crypto_1.createHash)('sha256').update(token).digest();
const hmac = (0, crypto_1.createHmac)('sha256', secret).update(body).digest('hex');
return hmac === signature;
}
catch (error) {
return false;
}
}
/**
* Parse and validate webhook payload
*
* @param body - Raw request body as string
* @returns Parsed webhook payload
* @throws WebhookParseError if payload is invalid
*/
function parseWebhookPayload(body) {
if (!body) {
throw new WebhookParseError('Webhook body is empty');
}
let parsed;
try {
parsed = JSON.parse(body);
}
catch (error) {
throw new WebhookParseError('Invalid JSON in webhook body');
}
// Validate required webhook structure
if (!parsed || typeof parsed !== 'object') {
throw new WebhookParseError('Webhook payload must be an object');
}
if (!parsed.type || !parsed.timestamp) {
throw new WebhookParseError('Webhook payload missing required fields: type, timestamp');
}
// Validate it's an invoice payment webhook
if (!(0, types_1.isInvoicePaymentWebhook)(parsed)) {
throw new WebhookParseError(`Unsupported webhook type: ${parsed.type}`);
}
// Validate data structure
if (!parsed.data || typeof parsed.data !== 'object') {
throw new WebhookParseError('Webhook payload missing or invalid data field');
}
const requiredDataFields = ['id', 'amount', 'currency', 'status', 'payment'];
for (const field of requiredDataFields) {
if (!(field in parsed.data)) {
throw new WebhookParseError(`Webhook data missing required field: ${field}`);
}
}
// Validate payment object
if (!parsed.data.payment || typeof parsed.data.payment !== 'object') {
throw new WebhookParseError('Webhook data missing or invalid payment field');
}
const requiredPaymentFields = ['userId', 'paymentNum', 'paymentAmount', 'paid'];
for (const field of requiredPaymentFields) {
if (!(field in parsed.data.payment)) {
throw new WebhookParseError(`Webhook payment data missing required field: ${field}`);
}
}
return parsed;
}
/**
* Verify signature and parse webhook payload in one step
*
* @param body - Raw request body as string
* @param signature - The rocket-pay-signature header value
* @param secret - Your webhook secret key
* @returns Parsed webhook payload
* @throws WebhookSignatureError if signature is invalid
* @throws WebhookParseError if payload is invalid
*/
function verifyAndParseWebhook(body, signature, secret) {
// Verify signature first
if (!verifyWebhookSignature(body, signature, secret)) {
throw new WebhookSignatureError('Invalid webhook signature');
}
// Parse and validate payload
return parseWebhookPayload(body);
}
/**
* Check if a webhook payload represents a paid invoice
*
* @param webhook - Parsed webhook payload
* @returns true if the invoice is paid
*/
function isInvoicePaid(webhook) {
return webhook.data.status === 'paid';
}
/**
* Extract payment information from webhook
*
* @param webhook - Parsed webhook payload
* @returns Payment summary object
*/
function extractPaymentInfo(webhook) {
const { data } = webhook;
return {
invoiceId: data.id,
amount: data.amount,
currency: data.currency,
status: data.status,
userId: data.payment.userId,
paymentAmount: data.payment.paymentAmount,
paymentAmountReceived: data.payment.paymentAmountReceived,
paymentNumber: data.payment.paymentNum,
paidAt: data.payment.paid,
comment: data.payment.comment || undefined,
payload: data.payload || undefined,
description: data.description || undefined,
activationsLeft: data.activationsLeft,
totalActivations: data.totalActivations
};
}