UNPKG

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
"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();