How to Integrate Secure Payments in Your Platform in Less Than 5 Lines of Code

Embed a secure M-Pesa checkout in any website in under 5 lines with the PalPluss Paylink widget — vanilla JS, React and CDN examples, plus server checks.

By
PalPluss Engineering
Published
Reading time
5 min

The short answer

  • The PalPluss Paylink widget (@palpluss/paylink) opens a hosted M-Pesa checkout from your site with a single pay() call — the whole integration fits in under 5 lines.
  • The checkout runs on link.palpluss.com inside an iframe, so your site never handles the customer's payment details or your API key.
  • For security, fix prices on the paylink itself and confirm each payment on your server before you deliver anything.

Not every team wants to build a checkout. If you run a course site, an event page, a SaaS dashboard or a WordPress store, you want customers to pay with M-Pesa today — without writing a backend payment flow, managing API keys in the browser or designing a payment form.

That is what PalPluss Paylinks are for. A paylink is a hosted M-Pesa payment page you create in the console. The @palpluss/paylink widget opens that page as a polished modal or inline embed on your own site — in fewer than five lines of code.

The whole integration

checkout.ts
import { pay } from "@palpluss/paylink";

const result = await pay("your-paylink-id");
console.log("Paid", result.amount, "ref", result.txId);

That is three lines. pay() opens a full-screen modal, the customer enters their M-Pesa number and approves the STK prompt on their phone, and the promise resolves with { txId, amount, phone }. If the customer closes the modal, it rejects — so wrap it in try/catch.

In the PalPluss console open Create Pay Link. Choose the payment channel, and decide whether the amount is fixed (a product with a price) or open (donations, tips, top-ups). Copy the paylink ID.

Step 2: Install the widget

terminal
npm install @palpluss/paylink

The core pay() function is plain DOM with zero dependencies. React is optional and only needed for the React component.

Step 3: Pick your style

Plain HTML — no build step

index.html
<script src="https://cdn.jsdelivr.net/npm/@palpluss/paylink@1/dist/cdn/palpluss-paylink.global.js"></script>
<button onclick="Palpluss.pay('your-paylink-id').then(r => alert('Paid! Ref: ' + r.txId))">
  Pay with M-Pesa
</button>

React button

CheckoutButton.tsx
import { pay } from "@palpluss/paylink";

export function CheckoutButton({ paylinkId }: { paylinkId: string }) {
  return (
    <button onClick={() => pay(paylinkId).then(onPaid).catch(() => {})}>
      Pay with M-Pesa
    </button>
  );
}

Pre-fill the amount and phone

Already know the cart total and the customer's number? Pass them in so the customer only has to approve. Since v1.4.0 the widget accepts amount, phone, customerName and lockPrefill.

checkout.ts
const result = await pay("your-paylink-id", {
  amount: 1500,
  phone: "0712345678",
  customerName: "Jane Wanjiku",
  lockPrefill: true,
});

Why this is secure

  • No secrets in the browser. The widget needs only a public paylink ID. Your API key stays on your server — or you don't need a server at all.
  • The checkout is isolated. The payment form runs on https://link.palpluss.com inside an iframe. Your page never sees or stores the customer's payment details.
  • Merchant prices always win. If the paylink has a fixed amount, any pre-filled amount is ignored — and the PalPluss API enforces it too, so a customer cannot change your price by editing the page.
  • Payment events are verified. From v1.4.1 the widget only accepts messages from its own iframe on https://link.palpluss.com, so other scripts or frames on your page cannot fake a successful payment.
  • Payments use M-Pesa's own PIN prompt. The customer approves on their phone, so card numbers and PINs never touch a web form.
  • Abuse controls are on by default — per-number request limits and success-rate monitoring stop prompt spam against your customers.
server/verify.ts
// Called by your frontend with result.txId after pay() resolves
const tx = await palpluss.getTransaction(txId);

if (tx.status === "SUCCESS" && tx.amount === order.total) {
  await grantAccess(order.id);
}

When to use the widget vs the API

You want…Use
A checkout on a website in minutes, no payment backendPaylink widget
Payments on WordPress, Webflow or a static sitePaylink widget (CDN script)
Full control of the flow inside your own app or USSDSTK Push API
Sending money out to customersB2C payouts API

Frequently asked questions

How do I accept M-Pesa payments on my website without a backend?

Create a paylink in the PalPluss console and open it with the @palpluss/paylink widget: import { pay } from '@palpluss/paylink' and call pay('your-paylink-id'). The hosted checkout handles the M-Pesa STK Push for you.

Is the PalPluss Paylink widget secure?

Yes. The checkout runs on link.palpluss.com inside an iframe, the browser only needs a public paylink ID, and fixed paylink prices are enforced by the API. For goods or access, confirm each payment server-side by webhook or transaction lookup before delivering.

Does the widget work with React?

Yes. Call pay() from any click handler, or use the PalplussPayment component from @palpluss/paylink/react for an inline embed. React is an optional peer dependency.

Can I pre-fill the amount and phone number?

Yes. From v1.4.0, pass amount, phone, customerName and lockPrefill in the pay() options. A fixed amount set on the paylink always takes priority.

Build on PalPluss

M-Pesa collections and payouts in one API. KES 15 in free tokens to start.