mpesa-sdk
Version:
Type safe SDK for M-Pesa API (Mozambique)
313 lines (309 loc) • 9.77 kB
JavaScript
// src/core.ts
import axios, { AxiosError } from "axios";
// src/utils.ts
import crypto from "crypto";
function getBearerToken(apiKey, publicKey) {
try {
const publicKeyBuffer = Buffer.from(publicKey, "base64");
const pk = crypto.createPublicKey({
key: publicKeyBuffer,
format: "der",
// Specify the format explicitly
type: "spki"
// Specify the type explicitly
});
const encryptedApiKeyBuffer = crypto.publicEncrypt(
{ key: pk, padding: crypto.constants.RSA_PKCS1_PADDING },
Buffer.from(apiKey, "utf-8")
);
return encryptedApiKeyBuffer.toString("base64");
} catch (error) {
return null;
}
}
function getApiBaseUrl(mode) {
const urlSandbox = "https://api.sandbox.vm.co.mz";
const urlProduction = "https://api.vm.co.mz";
return mode === "production" ? `${urlProduction}` : `${urlSandbox}`;
}
// src/error.ts
var MpesaResponseError = class extends Error {
statusCode;
data;
constructor(message, statusCode, data) {
super(message);
this.statusCode = statusCode;
this.data = data;
}
};
// src/core.ts
var MPesa = class {
configuration;
constructor(config) {
this.configuration = {
mode: config.mode ?? "sandbox",
apiKey: config.apiKey,
publicKey: config.publicKey,
origin: config.origin,
serviceProviderCode: config.serviceProviderCode
};
}
getConfiguration() {
return Object.assign({}, this.configuration);
}
updateConfiguration(config) {
this.configuration.mode = config.mode ?? this.configuration.mode;
this.configuration.apiKey = config.apiKey ?? this.configuration.apiKey;
this.configuration.publicKey = config.publicKey ?? this.configuration.publicKey;
this.configuration.origin = config.origin ?? this.configuration.origin;
this.configuration.serviceProviderCode = config.serviceProviderCode ?? this.configuration.serviceProviderCode;
}
async c2bPayment(request) {
const url = `${getApiBaseUrl(
this.configuration.mode
)}:18352/ipg/v1x/c2bPayment/singleStage/`;
const token = await getBearerToken(
this.configuration.apiKey,
this.configuration.publicKey
);
const headers = {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
Origin: this.configuration.origin
};
const body = {
input_TransactionReference: request.transactionReference,
input_CustomerMSISDN: request.msisdn,
input_Amount: request.amount + "",
input_ThirdPartyReference: request.thirdPartyReference,
input_ServiceProviderCode: this.configuration.serviceProviderCode
};
try {
const { data } = await axios.request({
method: "POST",
url,
headers,
data: body,
timeout: 12e4
});
return data;
} catch (error) {
if (error instanceof AxiosError) {
if (error.response?.status && error.response.status < 500) {
return error.response.data;
}
const message = error.message;
const statusCode = error.response?.status ?? 500;
const data = error.response?.data;
throw new MpesaResponseError(message, statusCode, data);
}
throw new Error("API error");
}
}
async b2cPayment(request) {
const url = `${getApiBaseUrl(
this.configuration.mode
)}:18345/ipg/v1x/b2cPayment/`;
const token = await getBearerToken(
this.configuration.apiKey,
this.configuration.publicKey
);
const headers = {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
Origin: this.configuration.origin
};
const body = {
input_TransactionReference: request.transactionReference,
input_CustomerMSISDN: request.msisdn,
input_Amount: request.amount + "",
input_ThirdPartyReference: request.thirdPartyReference,
input_ServiceProviderCode: this.configuration.serviceProviderCode
};
try {
const { data } = await axios.request({
method: "POST",
url,
headers,
data: body
});
return data;
} catch (error) {
if (error instanceof AxiosError) {
if (error.response?.status && error.response.status < 500) {
return error.response.data;
}
const message = error.message;
const statusCode = error.response?.status ?? 500;
const data = error.response?.data;
throw new MpesaResponseError(message, statusCode, data);
}
throw new Error("API error");
}
}
async b2bPayment(request) {
const url = `${getApiBaseUrl(
this.configuration.mode
)}:18349/ipg/v1x/b2bPayment/`;
const token = await getBearerToken(
this.configuration.apiKey,
this.configuration.publicKey
);
const headers = {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
Origin: this.configuration.origin
};
const body = {
input_TransactionReference: request.transactionReference,
input_Amount: request.amount + "",
input_ThirdPartyReference: request.thirdPartyReference,
input_PrimaryPartyCode: this.configuration.serviceProviderCode,
input_ReceiverPartyCode: request.receiverPartyCode
};
try {
const { data } = await axios.request({
method: "POST",
url,
headers,
data: body
});
return data;
} catch (error) {
if (error instanceof AxiosError) {
if (error.response?.status && error.response.status < 500) {
return error.response.data;
}
const message = error.message;
const statusCode = error.response?.status ?? 500;
const data = error.response?.data;
throw new MpesaResponseError(message, statusCode, data);
}
throw new Error("API error");
}
}
async queryTransactionStatus(request) {
const url = `${getApiBaseUrl(
this.configuration.mode
)}:18353/ipg/v1x/queryTransactionStatus/`;
const token = await getBearerToken(
this.configuration.apiKey,
this.configuration.publicKey
);
const headers = {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
Origin: this.configuration.origin
};
const body = {
input_ThirdPartyReference: request.thirdPartyReference,
input_QueryReference: request.queryReference,
input_ServiceProviderCode: this.configuration.serviceProviderCode
};
try {
const { data } = await axios.request({
method: "GET",
url,
headers,
params: body
});
return data;
} catch (error) {
if (error instanceof AxiosError) {
if (error.response?.status && error.response.status < 500) {
return error.response.data;
}
const message = error.message;
const statusCode = error.response?.status ?? 500;
const data = error.response?.data;
throw new MpesaResponseError(message, statusCode, data);
}
throw new Error("API error");
}
}
async queryCustomerName(request) {
const url = `${getApiBaseUrl(
this.configuration.mode
)}:19323/ipg/v1x/queryCustomerName/`;
const token = await getBearerToken(
this.configuration.apiKey,
this.configuration.publicKey
);
const headers = {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
Origin: this.configuration.origin
};
const body = {
input_CustomerMSISDN: request.msisdn,
input_ThirdPartyReference: request.thirdPartyReference,
input_ServiceProviderCode: this.configuration.serviceProviderCode
};
try {
const { data } = await axios.request({
method: "GET",
url,
headers,
params: body
});
return data;
} catch (error) {
if (error instanceof AxiosError) {
if (error.response?.status && error.response.status < 500) {
return error.response.data;
}
const message = error.message;
const statusCode = error.response?.status ?? 500;
const data = error.response?.data;
throw new MpesaResponseError(message, statusCode, data);
}
throw new Error("API error");
}
}
async reversal(request) {
const url = `${getApiBaseUrl(
this.configuration.mode
)}:18354/ipg/v1x/reversal/`;
const token = await getBearerToken(
this.configuration.apiKey,
this.configuration.publicKey
);
const headers = {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
Origin: this.configuration.origin
};
const body = {
input_TransactionID: request.transactionId,
input_SecurityCredential: request.securityCredential,
input_InitiatorIdentifier: request.initiatorIdentifier,
input_ThirdPartyReference: request.thirdPartyReference,
input_ServiceProviderCode: this.configuration.serviceProviderCode,
input_ReversalAmount: request.reversalAmount ? request.reversalAmount + "" : void 0
};
try {
const { data } = await axios.request({
method: "PUT",
url,
headers,
data: body
});
return data;
} catch (error) {
if (error instanceof AxiosError) {
if (error.response?.status && error.response.status < 500) {
return error.response.data;
}
const message = error.message;
const statusCode = error.response?.status ?? 500;
const data = error.response?.data;
throw new MpesaResponseError(message, statusCode, data);
}
throw new Error("API error");
}
}
};
export {
MpesaResponseError,
MPesa as default
};