Singularity Payments LogoSingularity Payments
Integrations

SvelteKit

Singularity payments can be integrated into your SvelteKit application with ease.

Install the packages

npm install @singularity-payments/sveltekit @singularity-payments/svelte
pnpm add @singularity-payments/sveltekit @singularity-payments/svelte
yarn add @singularity-payments/sveltekit @singularity-payments/svelte
bun add @singularity-payments/sveltekit @singularity-payments/svelte

Install 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 ngrok
pnpm add -g ngrok
yarn add -g ngrok
bun add -g ngrok

Guide on Obtaining M-pesa Credentials

Learn how to obtain M-pesa credentials for your application.

Configure your environmental variables

.env.local
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/result

Understanding 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:

  1. Submit business registration documents
  2. Complete KYC verification
  3. Wait for approval (can take several days)
  4. 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 number

Configure the Mpesa SDK on the Server

lib/mpesa.ts
import { createMpesa } from "@singularity-payments/sveltekit";
import { env } from "$env/dynamic/private";

export const mpesa = createMpesa(
  {
    consumerKey: env.MPESA_CONSUMER_KEY,
    consumerSecret: env.MPESA_CONSUMER_SECRET,
    passkey: env.MPESA_PASSKEY,
    shortcode: env.MPESA_SHORTCODE,
    environment:
      (env.MPESA_ENVIRONMENT as "sandbox" | "production") || "sandbox",
    callbackUrl: env.MPESA_CALLBACK_URL,
    initiatorName: "testapi",
    securityCredential: env.MPESA_SECURITY_CREDENTIAL,
    resultUrl: env.MPESA_RESULT_URL,
    timeoutUrl: env.MPESA_TIMEOUT_URL,
  },
  {
    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

src/routes/api/mpesa/[...path]/+server.ts
import { mpesa } from "$lib/mpesa";

export const POST = mpesa.handlers.catchAll.POST;

Configure the Mpesa SDK on the Client

lib/mpesa-client.ts
import { createMpesaClient } from "@singularity-payments/svelte";

export const mpesaClient = createMpesaClient({
  baseUrl: "", //If they are running on different ports you can include eg("http://localhost:3000").
});

Start Ngrok

The Daraja api requires a publically accessible callback url for callbacks

ngrok http 3000 # whatever port your server is running on

Copy paste the url from ngrok into the env variable MPESA_CALLBACK_URL.

.env.local
MPESA_CALLBACK_URL=https://your-ngrok-url.ngrok.free/api/mpesa/callback # make sure you include the /api/mpesa/callback

Use the Mpesa SDK on the Client

<script>
	import { mpesaClient } from '$lib/mpesa-client';

	let amount = 1;
	let phoneNumber = '254712345678'; // Default Phone Number Provided, you can change it to your own to get the request
	let loading = false;
	let response = null;
	let error = null;

	async function pay() {
		loading = true;
		error = null;
		response = null;

		try {
			const result = await mpesaClient.stkPush({
				amount,
				phoneNumber,
				accountReference: 'Singularity',
				transactionDesc: 'Singularity Payments'
			});
			response = result;
			console.log(result);
		} catch (err) {
			error = err.message || 'Payment failed';
			console.error(err);
		} finally {
			loading = false;
		}
	}
</script>

<div class="container">
	<h1>M-Pesa Payment</h1>

	<form on:submit|preventDefault={pay}>
		<div class="form-group">
			<label for="phone">Phone Number</label>
			<input id="phone" type="tel" bind:value={phoneNumber} placeholder="254712345678" required />
		</div>

		<div class="form-group">
			<label for="amount">Amount (KES)</label>
			<input id="amount" type="number" bind:value={amount} min="1" required />
		</div>

		<button type="submit" disabled={loading}>
			{loading ? 'Processing...' : 'Pay Now'}
		</button>
	</form>

	{#if response}
		<div class="success">Payment request sent successfully! Check your phone.</div>
	{/if}

	{#if error}
		<div class="error">
			{error}
		</div>
	{/if}
</div>

The core part of the SDK working here is

const { data, error } = await mpesaClient.stkPush({
  amount,
  phoneNumber,
  accountReference: "Singularity",
  transactionDesc: "Singularity Payment",
});
Edit on GitHub

On this page