@orenda-inc/nestjs-webx-pay
Version:
NestJS module for WebX Pay integration
201 lines (200 loc) • 9.68 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebXPayController = void 0;
const common_1 = require("@nestjs/common");
const payment_dto_1 = require("../dto/payment.dto");
const webx_pay_service_1 = require("../services/webx-pay.service");
let WebXPayController = class WebXPayController {
constructor(webxPayService, config) {
this.webxPayService = webxPayService;
this.config = config;
}
/**
* Process payment request
*/
async processPayment(paymentDto, req) {
try {
const result = await this.webxPayService.processPayment(paymentDto);
if (!result.success) {
throw new common_1.HttpException({ message: result.error, success: false }, common_1.HttpStatus.BAD_REQUEST);
}
this.config.onPaymentInitiated?.(req, paymentDto);
return result;
}
catch (error) {
this.config.onPaymentInitiatedFailed?.(req, paymentDto);
throw new common_1.HttpException({
message: error instanceof Error ? error.message : 'Payment processing failed',
success: false
}, common_1.HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/**
* Handle webhook notifications
*/
async handleWebhook(req, res, contentType = 'application/x-www-form-urlencoded') {
// WebX Pay sends data in the request body
let webhookData = {};
// Extract data from request body (this is the actual WebX Pay format)
if (req.body && typeof req.body === 'object') {
Object.entries(req.body).forEach(([key, value]) => {
if (typeof value === 'string') {
webhookData[key] = value;
}
else if (value !== null && value !== undefined) {
webhookData[key] = value.toString();
}
});
}
// If body is empty, try query parameters as fallback
if (Object.keys(webhookData).length === 0) {
Object.entries(req.query).forEach(([key, value]) => {
if (typeof value === 'string') {
webhookData[key] = value;
}
else if (Array.isArray(value)) {
webhookData[key] = value.join(', ');
}
});
}
// Decode the payment data if present
let decodedPaymentData = {};
if (webhookData.payment) {
try {
// Decode base64 payment data
const decodedPayment = Buffer.from(webhookData.payment, 'base64').toString('utf8');
//payment format: order_id|order_refference_number|date_time_transaction|payment_gateway_used|status_code|comment;
// Split and map payment fields with fallback for missing fields
const [orderId, referenceNumber, timestamp, gatewayId, statusCode, ...rest] = decodedPayment.split('|');
decodedPaymentData = {
orderId: orderId?.trim() || '',
referenceNumber: referenceNumber?.trim() || '',
timestamp: timestamp?.trim() || '',
gatewayId: gatewayId?.trim() || '',
statusCode: statusCode?.trim() || '',
comment: rest.length > 0 ? rest.join('|').trim() : ''
};
webhookData.payment = decodedPaymentData;
// Execute custom callback functions
try {
// Always call the general webhook callback if provided
if (this.config.onWebhookReceived) {
await this.config.onWebhookReceived(req, webhookData);
}
// Determine success/failure based on status code
const isSuccess = webhookData.status_code === '00' ||
decodedPaymentData.statusCode === '00';
// Call specific success/failure callbacks based on status
if (isSuccess) {
if (this.config.onPaymentSuccess) {
await this.config.onPaymentSuccess(req, webhookData);
}
}
else {
if (this.config.onPaymentFailure) {
await this.config.onPaymentFailure(req, webhookData);
}
}
}
catch (callbackError) {
console.error('Callback execution error:', callbackError);
// Continue processing even if callback fails
}
// Check if redirect URLs are configured
const hasRedirectUrls = this.config.redirectOnSuccess && this.config.redirectOnFailure;
// Determine success/failure for redirect
const isSuccess = webhookData.status_code === '00' ||
decodedPaymentData.statusCode === '00';
delete webhookData.signature;
delete webhookData.custom_fields;
if (!isSuccess) {
if (hasRedirectUrls) {
// Build failure redirect URL with error parameters
res.redirect(this.webxPayService.appendParamsToUrl(this.config.redirectOnFailure, webhookData));
return;
}
else {
// Return JSON response
return res.status(400).json({
success: false,
status: 'failed',
data: webhookData,
timestamp: new Date().toISOString()
});
}
}
if (hasRedirectUrls) {
console.log();
// Build success redirect URL with webhook data
res.redirect(this.webxPayService.appendParamsToUrl(this.config.redirectOnSuccess, webhookData));
return;
}
else {
// Return JSON response
return res.status(200).json({
success: true,
status: 'success',
data: webhookData,
timestamp: new Date().toISOString()
});
}
}
catch (error) {
const hasRedirectUrls = this.config.redirectOnSuccess && this.config.redirectOnFailure;
if (hasRedirectUrls) {
// Build failure redirect URL with error parameters
const failureUrl = new URL(this.config.redirectOnFailure);
failureUrl.searchParams.set('status', 'error');
failureUrl.searchParams.set('error', error instanceof Error ? error.message : 'Webhook processing failed');
failureUrl.searchParams.set('timestamp', new Date().toISOString());
res.redirect(failureUrl.toString());
return;
}
else {
// Return JSON error response
return res.status(500).json({
success: false,
status: 'error',
error: error instanceof Error ? error.message : 'Webhook processing failed',
timestamp: new Date().toISOString()
});
}
}
}
}
};
exports.WebXPayController = WebXPayController;
__decorate([
(0, common_1.Post)('payment'),
__param(0, (0, common_1.Body)()),
__param(1, (0, common_1.Req)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [payment_dto_1.PaymentRequestDto, Object]),
__metadata("design:returntype", Promise)
], WebXPayController.prototype, "processPayment", null);
__decorate([
(0, common_1.Post)('webhook'),
__param(0, (0, common_1.Req)()),
__param(1, (0, common_1.Res)()),
__param(2, (0, common_1.Headers)('content-type')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object, String]),
__metadata("design:returntype", Promise)
], WebXPayController.prototype, "handleWebhook", null);
exports.WebXPayController = WebXPayController = __decorate([
(0, common_1.Controller)('webx-pay'),
__param(1, (0, common_1.Inject)('WEBX_PAY_CONFIG')),
__metadata("design:paramtypes", [webx_pay_service_1.WebXPayService, Object])
], WebXPayController);