add(feat): verification
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
```dotenv
|
|
||||||
PORT=
|
PORT=
|
||||||
NODE_ENV=
|
NODE_ENV=
|
||||||
|
|
||||||
@@ -16,4 +15,7 @@ DB_USER=
|
|||||||
DB_PASS=
|
DB_PASS=
|
||||||
DB_NAME=
|
DB_NAME=
|
||||||
DB_DIALECT=
|
DB_DIALECT=
|
||||||
```
|
|
||||||
|
# Router Configuration
|
||||||
|
ROUTER_CALLBACK_TOKEN=
|
||||||
|
PROJECT_ID=
|
||||||
@@ -16,10 +16,13 @@ class BankTransferService {
|
|||||||
payload.amount = amount;
|
payload.amount = amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const projectId = process.env.PROJECT_ID;
|
||||||
|
const midtransOrderId = projectId ? `${external_id}-${projectId}` : external_id;
|
||||||
|
|
||||||
const parameter = {
|
const parameter = {
|
||||||
payment_type: "bank_transfer",
|
payment_type: "bank_transfer",
|
||||||
transaction_details: {
|
transaction_details: {
|
||||||
order_id: external_id,
|
order_id: midtransOrderId,
|
||||||
gross_amount: amount
|
gross_amount: amount
|
||||||
},
|
},
|
||||||
bank_transfer: {
|
bank_transfer: {
|
||||||
|
|||||||
@@ -16,10 +16,13 @@ class CreditCardService {
|
|||||||
payload.amount = amount;
|
payload.amount = amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const projectId = process.env.PROJECT_ID;
|
||||||
|
const midtransOrderId = projectId ? `${external_id}-${projectId}` : external_id;
|
||||||
|
|
||||||
const parameter = {
|
const parameter = {
|
||||||
payment_type: "credit_card",
|
payment_type: "credit_card",
|
||||||
transaction_details: {
|
transaction_details: {
|
||||||
order_id: external_id,
|
order_id: midtransOrderId,
|
||||||
gross_amount: amount
|
gross_amount: amount
|
||||||
},
|
},
|
||||||
credit_card: {
|
credit_card: {
|
||||||
|
|||||||
@@ -16,10 +16,13 @@ class CStoreService {
|
|||||||
payload.amount = amount;
|
payload.amount = amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const projectId = process.env.PROJECT_ID;
|
||||||
|
const midtransOrderId = projectId ? `${external_id}-${projectId}` : external_id;
|
||||||
|
|
||||||
const parameter = {
|
const parameter = {
|
||||||
payment_type: store,
|
payment_type: store,
|
||||||
transaction_details: {
|
transaction_details: {
|
||||||
order_id: external_id,
|
order_id: midtransOrderId,
|
||||||
gross_amount: amount
|
gross_amount: amount
|
||||||
},
|
},
|
||||||
customer_details: customer_details,
|
customer_details: customer_details,
|
||||||
|
|||||||
@@ -16,10 +16,13 @@ class EWalletService {
|
|||||||
payload.amount = amount;
|
payload.amount = amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const projectId = process.env.PROJECT_ID;
|
||||||
|
const midtransOrderId = projectId ? `${external_id}-${projectId}` : external_id;
|
||||||
|
|
||||||
const parameter = {
|
const parameter = {
|
||||||
payment_type: type,
|
payment_type: type,
|
||||||
transaction_details: {
|
transaction_details: {
|
||||||
order_id: external_id,
|
order_id: midtransOrderId,
|
||||||
gross_amount: amount
|
gross_amount: amount
|
||||||
},
|
},
|
||||||
customer_details: customer_details,
|
customer_details: customer_details,
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ class NotificationController {
|
|||||||
const payload = req.body;
|
const payload = req.body;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const callbackToken = req.headers['x-callback-token'];
|
||||||
|
if (callbackToken !== process.env.ROUTER_CALLBACK_TOKEN) {
|
||||||
|
return responseHelper.error(res, "Unauthorized token router gateway", 401);
|
||||||
|
}
|
||||||
|
|
||||||
const isValid = notificationService.verifySignature(payload);
|
const isValid = notificationService.verifySignature(payload);
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
return responseHelper.error(res, "Invalid Signature Key", 403);
|
return responseHelper.error(res, "Invalid Signature Key", 403);
|
||||||
|
|||||||
@@ -16,10 +16,13 @@ class PayLaterService {
|
|||||||
payload.amount = amount;
|
payload.amount = amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const projectId = process.env.PROJECT_ID;
|
||||||
|
const midtransOrderId = projectId ? `${external_id}-${projectId}` : external_id;
|
||||||
|
|
||||||
const parameter = {
|
const parameter = {
|
||||||
payment_type: type,
|
payment_type: type,
|
||||||
transaction_details: {
|
transaction_details: {
|
||||||
order_id: external_id,
|
order_id: midtransOrderId,
|
||||||
gross_amount: amount
|
gross_amount: amount
|
||||||
},
|
},
|
||||||
customer_details: customer_details,
|
customer_details: customer_details,
|
||||||
|
|||||||
@@ -16,9 +16,12 @@ class SnapService {
|
|||||||
payload.amount = amount;
|
payload.amount = amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const projectId = process.env.PROJECT_ID;
|
||||||
|
const midtransOrderId = projectId ? `${external_id}-${projectId}` : external_id;
|
||||||
|
|
||||||
const parameter = {
|
const parameter = {
|
||||||
transaction_details: {
|
transaction_details: {
|
||||||
order_id: external_id,
|
order_id: midtransOrderId,
|
||||||
gross_amount: amount
|
gross_amount: amount
|
||||||
},
|
},
|
||||||
customer_details: customer_details,
|
customer_details: customer_details,
|
||||||
|
|||||||
@@ -21,13 +21,18 @@ describe('BankTransferService', () => {
|
|||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
process.env.MIDTRANS_EXPIRY_DURATION = '1';
|
process.env.MIDTRANS_EXPIRY_DURATION = '1';
|
||||||
process.env.MIDTRANS_EXPIRY_UNIT = 'hour';
|
process.env.MIDTRANS_EXPIRY_UNIT = 'hour';
|
||||||
|
process.env.PROJECT_ID = 'CBT';
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a bank transfer transaction (BCA) successfully', async () => {
|
it('should create a bank transfer transaction (BCA) successfully', async () => {
|
||||||
const payload = { order_id: 'u-123', external_id: 'INV-001', amount: 10000 };
|
const payload = { order_id: 'u-123', external_id: 'INV-001', amount: 10000 };
|
||||||
mockCharge.mockResolvedValue({ status_code: '201' });
|
mockCharge.mockResolvedValue({ status_code: '201' });
|
||||||
const result = await bankTransferService.createTransaction(payload, 'bca');
|
const result = await bankTransferService.createTransaction(payload, 'bca');
|
||||||
expect(mockCharge).toHaveBeenCalledWith(expect.objectContaining({ payment_type: 'bank_transfer' }));
|
expect(mockCharge).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
|
transaction_details: expect.objectContaining({
|
||||||
|
order_id: 'INV-001-CBT'
|
||||||
|
})
|
||||||
|
}));
|
||||||
expect(result.status_code).toBe('201');
|
expect(result.status_code).toBe('201');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -18,13 +18,18 @@ const creditCardService = require('../../app/modules/credit-card/services/credit
|
|||||||
describe('CreditCardService', () => {
|
describe('CreditCardService', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
process.env.PROJECT_ID = 'CBT';
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a credit card transaction successfully', async () => {
|
it('should create a credit card transaction successfully', async () => {
|
||||||
const payload = { order_id: 'u-cc', external_id: 'INV-CC-001', amount: 500000, credit_card_token: 't-123' };
|
const payload = { order_id: 'u-cc', external_id: 'INV-CC-001', amount: 500000, credit_card_token: 't-123' };
|
||||||
mockCharge.mockResolvedValue({ status_code: '201', transaction_status: 'capture' });
|
mockCharge.mockResolvedValue({ status_code: '201', transaction_status: 'capture' });
|
||||||
const result = await creditCardService.createTransaction(payload);
|
const result = await creditCardService.createTransaction(payload);
|
||||||
expect(mockCharge).toHaveBeenCalled();
|
expect(mockCharge).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
|
transaction_details: expect.objectContaining({
|
||||||
|
order_id: 'INV-CC-001-CBT'
|
||||||
|
})
|
||||||
|
}));
|
||||||
expect(result.status_code).toBe('201');
|
expect(result.status_code).toBe('201');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -17,13 +17,18 @@ const cstoreService = require('../../app/modules/cstore/services/cstore.service'
|
|||||||
describe('CStoreService', () => {
|
describe('CStoreService', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
process.env.PROJECT_ID = 'CBT';
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create an indomaret transaction successfully', async () => {
|
it('should create an indomaret transaction successfully', async () => {
|
||||||
const payload = { order_id: 'u-123', external_id: 'INV-I-001', amount: 50000 };
|
const payload = { order_id: 'u-123', external_id: 'INV-I-001', amount: 50000 };
|
||||||
mockCharge.mockResolvedValue({ status_code: '201' });
|
mockCharge.mockResolvedValue({ status_code: '201' });
|
||||||
const result = await cstoreService.createTransaction(payload, 'indomaret');
|
const result = await cstoreService.createTransaction(payload, 'indomaret');
|
||||||
expect(mockCharge).toHaveBeenCalled();
|
expect(mockCharge).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
|
transaction_details: expect.objectContaining({
|
||||||
|
order_id: 'INV-I-001-CBT'
|
||||||
|
})
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create an alfamart transaction successfully', async () => {
|
it('should create an alfamart transaction successfully', async () => {
|
||||||
|
|||||||
@@ -17,13 +17,18 @@ const eWalletService = require('../../app/modules/e-wallet/services/e-wallet.ser
|
|||||||
describe('EWalletService', () => {
|
describe('EWalletService', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
process.env.PROJECT_ID = 'CBT';
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a gopay transaction successfully', async () => {
|
it('should create a gopay transaction successfully', async () => {
|
||||||
const payload = { order_id: 'u-123', external_id: 'INV-G-001', amount: 25000 };
|
const payload = { order_id: 'u-123', external_id: 'INV-G-001', amount: 25000 };
|
||||||
mockCharge.mockResolvedValue({ status_code: '201' });
|
mockCharge.mockResolvedValue({ status_code: '201' });
|
||||||
const result = await eWalletService.createTransaction(payload, 'gopay');
|
const result = await eWalletService.createTransaction(payload, 'gopay');
|
||||||
expect(mockCharge).toHaveBeenCalled();
|
expect(mockCharge).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
|
transaction_details: expect.objectContaining({
|
||||||
|
order_id: 'INV-G-001-CBT'
|
||||||
|
})
|
||||||
|
}));
|
||||||
expect(result.status_code).toBe('201');
|
expect(result.status_code).toBe('201');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8,17 +8,20 @@ const NotificationController = require('../../app/modules/notification/controlle
|
|||||||
|
|
||||||
describe('NotificationController', () => {
|
describe('NotificationController', () => {
|
||||||
let mockReq, mockRes;
|
let mockReq, mockRes;
|
||||||
|
const routerToken = 'TEST_ROUTER_TOKEN';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
process.env.ROUTER_CALLBACK_TOKEN = routerToken;
|
||||||
mockRes = {
|
mockRes = {
|
||||||
status: jest.fn().mockReturnThis(),
|
status: jest.fn().mockReturnThis(),
|
||||||
json: jest.fn().mockReturnThis()
|
json: jest.fn().mockReturnThis()
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle notification successfully', async () => {
|
it('should handle notification successfully when token and signature are valid', async () => {
|
||||||
mockReq = {
|
mockReq = {
|
||||||
|
headers: { 'x-callback-token': routerToken },
|
||||||
body: { order_id: 'I-123', transaction_status: 'settlement' }
|
body: { order_id: 'I-123', transaction_status: 'settlement' }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,8 +35,22 @@ describe('NotificationController', () => {
|
|||||||
expect(responseHelper.success).toHaveBeenCalledWith(mockRes, expect.stringContaining('Handled'));
|
expect(responseHelper.success).toHaveBeenCalledWith(mockRes, expect.stringContaining('Handled'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return 401 if x-callback-token is invalid', async () => {
|
||||||
|
mockReq = {
|
||||||
|
headers: { 'x-callback-token': 'wrong-token' },
|
||||||
|
body: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
await NotificationController.handle(mockReq, mockRes);
|
||||||
|
|
||||||
|
expect(responseHelper.error).toHaveBeenCalledWith(mockRes, expect.stringContaining('Unauthorized token'), 401);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return 403 if signature invalid', async () => {
|
it('should return 403 if signature invalid', async () => {
|
||||||
mockReq = { body: { signature_key: 'invalid' } };
|
mockReq = {
|
||||||
|
headers: { 'x-callback-token': routerToken },
|
||||||
|
body: { signature_key: 'invalid' }
|
||||||
|
};
|
||||||
notificationService.verifySignature.mockReturnValue(false);
|
notificationService.verifySignature.mockReturnValue(false);
|
||||||
|
|
||||||
await NotificationController.handle(mockReq, mockRes);
|
await NotificationController.handle(mockReq, mockRes);
|
||||||
@@ -42,7 +59,10 @@ describe('NotificationController', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return error on service error', async () => {
|
it('should return error on service error', async () => {
|
||||||
mockReq = { body: { order_id: 'I-123' } };
|
mockReq = {
|
||||||
|
headers: { 'x-callback-token': routerToken },
|
||||||
|
body: { order_id: 'I-123' }
|
||||||
|
};
|
||||||
notificationService.verifySignature.mockReturnValue(true);
|
notificationService.verifySignature.mockReturnValue(true);
|
||||||
notificationService.processNotification.mockRejectedValue(new Error('DB Error'));
|
notificationService.processNotification.mockRejectedValue(new Error('DB Error'));
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const paylaterService = require('../../app/modules/paylater/services/paylater.se
|
|||||||
describe('PayLaterService', () => {
|
describe('PayLaterService', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
process.env.PROJECT_ID = 'CBT';
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create an akulaku transaction successfully', async () => {
|
it('should create an akulaku transaction successfully', async () => {
|
||||||
@@ -25,6 +26,9 @@ describe('PayLaterService', () => {
|
|||||||
mockCharge.mockResolvedValue({ status_code: '201' });
|
mockCharge.mockResolvedValue({ status_code: '201' });
|
||||||
await paylaterService.createTransaction(payload, 'akulaku');
|
await paylaterService.createTransaction(payload, 'akulaku');
|
||||||
expect(mockCharge).toHaveBeenCalledWith(expect.objectContaining({
|
expect(mockCharge).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
|
transaction_details: expect.objectContaining({
|
||||||
|
order_id: 'INV-AK-001-CBT'
|
||||||
|
}),
|
||||||
payment_type: 'akulaku'
|
payment_type: 'akulaku'
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,13 +16,18 @@ const snapService = require('../../app/modules/snap/services/snap.service');
|
|||||||
describe('SnapService', () => {
|
describe('SnapService', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
process.env.PROJECT_ID = 'CBT';
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a snap transaction successfully', async () => {
|
it('should create a snap transaction successfully', async () => {
|
||||||
const payload = { order_id: 'u-snap', external_id: 'INV-S-001', amount: 100000 };
|
const payload = { order_id: 'u-snap', external_id: 'INV-S-001', amount: 100000 };
|
||||||
mockCreateTransaction.mockResolvedValue({ token: 't-123', redirect_url: 'http://m.com' });
|
mockCreateTransaction.mockResolvedValue({ token: 't-123', redirect_url: 'http://m.com' });
|
||||||
const result = await snapService.createTransaction(payload);
|
const result = await snapService.createTransaction(payload);
|
||||||
expect(mockCreateTransaction).toHaveBeenCalled();
|
expect(mockCreateTransaction).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
|
transaction_details: expect.objectContaining({
|
||||||
|
order_id: 'INV-S-001-CBT'
|
||||||
|
})
|
||||||
|
}));
|
||||||
expect(result.token).toBe('t-123');
|
expect(result.token).toBe('t-123');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user