mds-daraja-sdk
Version:
A simple SDK for integrating with Safaricom's Daraja API for M-Pesa payments.
306 lines (305 loc) • 12.9 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.safaricomDarajaApi = exports.SafaricomDarajaApi = void 0;
// safaricom-daraja.ts
const axios_1 = __importDefault(require("axios"));
const dotenv_1 = require("dotenv");
(0, dotenv_1.config)();
class SafaricomDarajaApi {
constructor(consumerKey = process.env.DARAJA_API_CONSUMER_KEY, consumerSecret = process.env.DARAJA_API_CONSUMER_SECRET, businessShortCode = process.env.DARAJA_API_BUSINESS_SHORT_CODE, passkey = process.env.DARAJA_API_PASS_KEY) {
this.businessShortCode = businessShortCode;
this.passkey = passkey;
this.consumerKey = consumerKey;
this.consumerSecret = consumerSecret;
this.baseUrl = process.env.DARAJA_API_BASE_URL; // Use 'https://api.safaricom.co.ke' for production
}
/**
* Get the access token from Safaricom Daraja API
*/
getAccessToken() {
return __awaiter(this, void 0, void 0, function* () {
const url = `${this.baseUrl}/oauth/v1/generate?grant_type=client_credentials`;
const auth = Buffer.from(`${this.consumerKey}:${this.consumerSecret}`).toString("base64");
const response = yield axios_1.default.get(url, {
headers: {
Authorization: `Basic ${auth}`,
},
});
return response.data.access_token;
});
}
/**
* Register C2B URLs
*/
registerUrls(confirmationUrl, validationUrl, shortCode = this.businessShortCode) {
return __awaiter(this, void 0, void 0, function* () {
const accessToken = yield this.getAccessToken();
const url = `${this.baseUrl}/mpesa/c2b/v1/registerurl`;
const requestBody = {
ShortCode: shortCode,
ResponseType: "Completed",
ConfirmationURL: confirmationUrl,
ValidationURL: validationUrl,
};
const response = yield axios_1.default.post(url, requestBody, {
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
});
return response.data;
});
}
/**
* Check Account Balance
*/
checkBalance(shortCode, password, timestamp, partyA) {
return __awaiter(this, void 0, void 0, function* () {
const accessToken = yield this.getAccessToken();
const url = `${this.baseUrl}/mpesa/accountbalance/v1/query`;
const requestBody = {
Initiator: shortCode,
SecurityCredential: password,
CommandID: "AccountBalance",
PartyA: partyA,
IdentifierType: "4",
Remarks: "Balance inquiry",
QueueTimeOutURL: "https://example.com/timeout",
ResultURL: "https://example.com/result", // Handle result callbacks
};
const response = yield axios_1.default.post(url, requestBody, {
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
});
return response.data;
});
}
/**
* Initiate an STK Push
*/
initiateStkPush(stkPushRequest) {
return __awaiter(this, void 0, void 0, function* () {
const accessToken = yield this.getAccessToken();
const url = `${this.baseUrl}/mpesa/stkpush/v1/processrequest`;
const response = yield axios_1.default.post(url, stkPushRequest, {
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
});
return response.data;
});
}
initiateC2bStkPush(phone, amount, accountReference, transactionDesc, callbackUrl = process.env.DARAJA_API_CALLBACK_URL) {
return __awaiter(this, void 0, void 0, function* () {
const accessToken = yield this.getAccessToken();
const url = `${this.baseUrl}/mpesa/stkpush/v1/processrequest`;
const password = SafaricomDarajaApi.generatePassword(this.businessShortCode, this.passkey);
const formatedPhone = phone.startsWith("0")
? `254${phone.substring(1)}`
: phone;
const stkPushRequestBody = {
BusinessShortCode: this.businessShortCode,
Password: password,
Timestamp: SafaricomDarajaApi.generateTimeStamp(),
TransactionType: "CustomerPayBillOnline",
Amount: amount,
PartyA: formatedPhone,
PartyB: this.businessShortCode,
PhoneNumber: formatedPhone,
CallBackURL: callbackUrl || "https://example.com/callback",
AccountReference: accountReference || process.env.DARAJA_API_APP_NAME || "Test Payment",
TransactionDesc: transactionDesc || "Payment",
};
const response = yield axios_1.default.post(url, stkPushRequestBody, {
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
});
return response.data;
});
}
/**
* Query a transaction status
*/
queryTransactionStatus(BusinessShortCode, Password, Timestamp, CheckoutRequestID) {
return __awaiter(this, void 0, void 0, function* () {
const accessToken = yield this.getAccessToken();
const url = `${this.baseUrl}/mpesa/stkpushquery/v1/query`;
const requestBody = {
BusinessShortCode,
Password,
Timestamp,
CheckoutRequestID,
};
const response = yield axios_1.default.post(url, requestBody, {
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
});
return response.data;
});
}
/**
* Transaction Reversal
*/
reverseTransaction(shortCode, password, timestamp, transactionID) {
return __awaiter(this, void 0, void 0, function* () {
const accessToken = yield this.getAccessToken();
const url = `${this.baseUrl}/mpesa/reversal/v1/request`;
const requestBody = {
Initiator: shortCode,
SecurityCredential: password,
CommandID: "TransactionReversal",
TransactionID: transactionID,
Amount: 1000,
ReceiverParty: shortCode,
RecieverIdentifierType: "11",
Remarks: "Reversal of transaction",
QueueTimeOutURL: "https://example.com/timeout",
ResultURL: "https://example.com/result", // Handle result callbacks
};
const response = yield axios_1.default.post(url, requestBody, {
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
});
return response.data;
});
}
/**
* B2C Payment Request
*/
b2cPayment(shortCode, password, amount, partyA, partyB) {
return __awaiter(this, void 0, void 0, function* () {
const accessToken = yield this.getAccessToken();
const url = `${this.baseUrl}/mpesa/b2c/v1/paymentrequest`;
const requestBody = {
InitiatorName: shortCode,
SecurityCredential: password,
CommandID: "BusinessPayment",
Amount: amount,
PartyA: partyA,
PartyB: partyB,
Remarks: "Payment from business",
QueueTimeOutURL: "https://example.com/timeout",
ResultURL: "https://example.com/result",
Occasion: "Payment for services",
};
const response = yield axios_1.default.post(url, requestBody, {
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
});
return response.data;
});
}
//============================================
/**
* Handles the final confirmation of the transaction after processing.
* @param confirmationData - The confirmation request data from M-Pesa
* @returns An object containing the status of the transaction
*/
handleTransactionConfirmation(confirmationData) {
const { ResultCode, TransAmount, MpesaReceiptNumber, BillRefNumber } = confirmationData;
// Check if the transaction was successful
if (ResultCode === 0) {
// Transaction successful, process the data
this.recordTransaction({
amount: TransAmount,
receiptNumber: MpesaReceiptNumber,
accountNumber: BillRefNumber,
});
return {
success: true,
message: "Transaction confirmed successfully",
data: {
amount: TransAmount,
receiptNumber: MpesaReceiptNumber,
},
};
}
else {
// Handle failed transaction
return {
success: false,
message: "Transaction failed",
};
}
}
recordTransaction(transactionData, cb) {
return __awaiter(this, void 0, void 0, function* () {
// Implement logic to save the transaction to your database or system
if (cb) {
yield cb(transactionData);
}
});
}
/**=======================================================================
* Handles validation of incoming transactions
* @param validationData - The validation request data from M-Pesa
* @returns A response indicating whether to accept or reject the transaction
*/
handleTransactionValidation(validationData) {
const { TransactionType, TransAmount, BillRefNumber } = validationData;
// Custom logic to check the validity of the transaction
if (TransactionType !== "PayBill" || TransAmount <= 0) {
return {
success: false,
message: "Invalid transaction type or amount",
};
}
// Check if BillRefNumber (account number) exists in your system
const accountExists = this.checkAccountExists(BillRefNumber);
if (!accountExists) {
return {
success: false,
message: "Account does not exist",
};
}
// Validation passed
return {
success: true,
message: "Transaction accepted",
};
}
checkAccountExists(accountNumber) {
// Implement your logic to check if the account exists in your system
return true; // Return true if valid, false if not
}
/**
* Generate password using short code, passkey, and timestamp
*/
static generatePassword(shortcode, passkey, timestamp = SafaricomDarajaApi.generateTimeStamp()) {
return Buffer.from(`${shortcode}${passkey}${timestamp}`).toString("base64");
}
static generateTimeStamp() {
const date = new Date();
return (date.getFullYear() +
("0" + (date.getMonth() + 1)).slice(-2) +
("0" + date.getDate()).slice(-2) +
("0" + date.getHours()).slice(-2) +
("0" + date.getMinutes()).slice(-2) +
("0" + date.getSeconds()).slice(-2));
}
}
exports.SafaricomDarajaApi = SafaricomDarajaApi;
exports.safaricomDarajaApi = new SafaricomDarajaApi();