Nuxt
Singularity payments can be integrated into your Nuxt application with ease.
Install the packages
npm install @singularity-payments/nuxt @singularity-payments/vuepnpm add @singularity-payments/nuxt @singularity-payments/vueyarn add @singularity-payments/nuxt @singularity-payments/vuebun add @singularity-payments/nuxt @singularity-payments/vueInstall Ngrok
The API requires a publically accessible url for callbacks. You can use ngrok to expose your local development server to the internet.
npm install -g ngrokpnpm add -g ngrokyarn add -g ngrokbun add -g ngrokGuide on Obtaining M-pesa Credentials
Learn how to obtain M-pesa credentials for your application.
Configure your environmental variables
MPESA_CONSUMER_KEY=your_consumer_key
MPESA_CONSUMER_SECRET=your_consumer_secret
MPESA_SHORTCODE=174379 #For STK Push Testing in Sandbox(Replace with your shortcode in production)
MPESA_PASSKEY=your_passkey
MPESA_ENVIRONMENT=sandbox # or 'production'
MPESA_CALLBACK_URL=https://your-app-url.com/api/mpesa/callback
# For STK Push you can just include the 6 above
# Required for B2C, B2B, Reversal, Transaction Status, and Account Balance
MPESA_INITIATOR_NAME=testapi #Initiator Name for Sandbox
MPESA_SECURITY_CREDENTIAL=your_security_credential
MPESA_RESULT_URL=https://your-app-url.com/api/mpesa/resultUnderstanding Shortcodes
Your shortcode is your business number in the M-Pesa system, it's what customers see when making payments.
Sandbox (Testing)
Use M-Pesa's provided test shortcodes:
174379- For STK Push testing
These are pre-configured and ready to use immediately.
Production (Live)
You'll need to apply for your own shortcode through Safaricom's M-Pesa portal:
- Submit business registration documents
- Complete KYC verification
- Wait for approval (can take several days)
- Receive your unique 5-7 digit shortcode
Important: Never mix sandbox and production credentials. Always use the matching shortcode for your environment.
# Sandbox
MPESA_SHORTCODE=174379
# Production
MPESA_SHORTCODE=123456 # Your approved business numberConfigure Nuxt Config
export default defineNuxtConfig({
compatibilityDate: "2025-07-15", // Keep this as it is in your own project
devtools: { enabled: true },
runtimeConfig: {
mpesaConsumerKey: process.env.MPESA_CONSUMER_KEY,
mpesaConsumerSecret: process.env.MPESA_CONSUMER_SECRET,
mpesaPasskey: process.env.MPESA_PASSKEY,
mpesaShortcode: process.env.MPESA_SHORTCODE,
mpesaEnvironment: process.env.MPESA_ENVIRONMENT,
mpesaCallbackUrl: process.env.MPESA_CALLBACK_URL,
mpesaInitiatorName: process.env.MPESA_INITIATOR_NAME,
mpesaSecurityCredential: process.env.MPESA_SECURITY_CREDENTIAL,
mpesaResultUrl: process.env.MPESA_RESULT_URL,
mpesaTimeoutUrl: process.env.MPESA_TIMEOUT_URL,
},
});Configure the Mpesa SDK on the Server
import { createMpesa } from "@singularity-payments/nuxt";
const config = useRuntimeConfig();
export const mpesa = createMpesa(
{
consumerKey: config.mpesaConsumerKey,
consumerSecret: config.mpesaConsumerSecret,
passkey: config.mpesaPasskey,
shortcode: config.mpesaShortcode,
environment:
(config.mpesaEnvironment as "sandbox" | "production") || "sandbox",
callbackUrl: config.mpesaCallbackUrl,
initiatorName: "testapi",
securityCredential: config.mpesaSecurityCredential,
resultUrl: config.mpesaResultUrl,
timeoutUrl: config.mpesaTimeoutUrl,
},
{
callbackOptions: {
onSuccess: async (data) => {
console.log("Payment successful:", {
amount: data.amount,
phone: data.phoneNumber,
receipt: data.mpesaReceiptNumber,
transactionDate: data.transactionDate,
});
// TODO: Save to database
},
onFailure: async (data) => {
console.log("Payment failed:", {
resultCode: data.resultCode,
resultDesc: data.resultDescription,
});
// TODO: Update database
},
},
},
);Configure the Catch All Route
import { mpesa } from "../../utils/mpesa";
export default mpesa.handlers.catchAll;Configure the Mpesa SDK on the Client
import { createMpesaClient } from "@singularity-payments/vue";
export const useMpesa = () => {
const mpesaClient = createMpesaClient({
baseUrl: "",
});
return {
mpesaClient,
};
};Start Ngrok
The Daraja api requires a publically accessible callback url for callbacks
ngrok http 3000 # whatever port your server is running onCopy paste the url from ngrok into the env variable MPESA_CALLBACK_URL.
MPESA_CALLBACK_URL=https://your-ngrok-url.ngrok.free/api/mpesa/callback # make sure you include the /api/mpesa/callbackUse the Mpesa SDK on the Client
<template>
<div class="container">
<h1>M-Pesa Payment</h1>
<form @submit.prevent="pay">
<div class="form-group">
<label for="phone">Phone Number</label>
<input
id="phone"
v-model="phoneNumber"
type="tel"
placeholder="254712345678"
required
/>
</div>
<div class="form-group">
<label for="amount">Amount (KES)</label>
<input id="amount" v-model="amount" type="number" min="1" required />
</div>
<button type="submit" :disabled="loading">
{{ loading ? "Processing..." : "Pay Now" }}
</button>
</form>
<div v-if="response" class="success">
Payment request sent successfully! Check your phone.
</div>
<div v-if="error" class="error">
{{ error }}
</div>
</div>
</template>
<script setup lang="ts">
import { useMpesa } from "../composables/useMpesa";
const { mpesaClient } = useMpesa();
const amount = ref(1);
const phoneNumber = ref("254712345678");
const loading = ref(false);
const response = ref(null);
const error = ref(null);
async function pay() {
loading.value = true;
error.value = null;
response.value = null;
try {
const result = await mpesaClient.stkPush({
amount: amount.value,
phoneNumber: phoneNumber.value,
accountReference: "Singularity",
transactionDesc: "Singularity Payments",
});
response.value = result;
console.log(result);
} catch (err: any) {
error.value = err.message || "Payment failed";
console.error(err);
} finally {
loading.value = false;
}
}
</script>The core part of the SDK working here is
const result = await mpesaClient.stkPush({
amount: amount.value,
phoneNumber: phoneNumber.value,
accountReference: "Singularity",
transactionDesc: "Singularity Payments",
});