npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

paymob-widget

v1.0.3

Published

`paymob-widget`

Readme

Paymob Installments Widget

paymob-widget

A drop-in widget you embed in any web page (product page, cart, or checkout) to show your customers the installment plans available for a given amount. You can use it purely to display plans, or let customers pick a plan and hand the selection back to your code.

No framework required — it works on any HTML page with a single <script> tag, and it also drops into React, Angular or Vue apps (see Use in a framework).


How it works

  1. You add the script and a container element to your page.
  2. You create a PaymobWidget with your public key and the order amount.
  3. The widget calls Paymob, fetches the installment plans for that amount, and renders them inside your container.

There are two modes:

| Mode | What the customer sees | When to use it | | --- | --- | --- | | Display only (default) | A read-only list of available installment plans. | You just want to promote installments (e.g. "Pay in 12 months") on a product page. | | Selectable (customerCanSelect: true) | The same plans, but the customer can select one and click Buy now. | You want the customer to choose a plan and continue the purchase in your own flow. |


Quick start

1. Add the script

Using the jsDelivr CDN:

<script src="https://cdn.jsdelivr.net/npm/paymob-widget@latest/main.js" type="module"></script>

2. Add a container

Place an empty element wherever you want the widget to appear:

<div id="paymob-widget"></div>

3. Create the widget

new PaymobWidget({
  publicKey: 'egy_pk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
  elementId: 'paymob-widget',
  amount: 100000, // order total in cents — this is 1,000 EGP
  currency: 'EGP',
});

That's it. If installment plans are available for that amount, the widget renders them.

⚠️ amount is in cents, not whole currency units. To show plans for 1,000 EGP, pass amount: 100000 (1,000 × 100). If amount is missing, zero, negative, or not a number, the widget will not render and an error is logged to the browser console.


Options

| Option | Type | Required | Description | | --- | --- | --- | --- | | publicKey | string | Yes | Your Paymob public key. Authenticates requests and selects the correct regional API (based on the key's country prefix, e.g. egy_, are_, sau_, pak_, omn_). | | elementId | string | Yes | The id of the HTML element the widget renders into. | | amount | number | Yes | The order total in cents (a positive number). No default — the widget won't render without it. | | currency | string | No | Currency code. Default: "EGP". | | integrationId | number \| number[] | No | One or more Paymob integration IDs. When provided, plans are fetched for those integrations only. | | theme | "primary" \| "light" \| "dark" | No | Visual theme. Default: "primary". | | customerCanSelect | boolean | No | Set to true to let the customer select a plan and enable Buy now. Default: false (read-only). | | onSubmit | function | No | Called when the customer clicks Buy now. See Handling the selection. |


Letting customers select a plan

Set customerCanSelect: true and provide an onSubmit callback. Your callback runs when the customer picks a plan and clicks Buy now, so you can continue the purchase in your own flow.

new PaymobWidget({
  publicKey: 'egy_pk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
  elementId: 'paymob-widget',
  amount: 100000, // 1,000 EGP
  currency: 'EGP',
  theme: 'primary',
  customerCanSelect: true,
  onSubmit: (plan) => {
    // plan = { id: 123, tenure: 12, amount: 25000 }
    console.log('Customer selected plan', plan);
    // → continue your purchase flow here
  },
});

Handling the selection

When customerCanSelect is true, onSubmit receives the selected plan:

{
  id: string | number,   // the installment plan ID
  tenure: number,        // number of months
  amount: number         // monthly installment amount, in cents
}

When customerCanSelect is false, the plans are read-only. Buy now is not part of that flow, and if onSubmit is called it receives no payload.

The built-in Learn more dialog also adapts: with customerCanSelect: true it includes a "Select installment plan" step; with false it omits that step.


Full HTML example

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Paymob Widget Example</title>
    <base href="/" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <style>
      #paymob-widget {
        width: 100%;
        margin: 10% auto 0;
      }
      body {
        margin: 0;
      }
    </style>
  </head>

  <body>
    <div id="paymob-widget"></div>

    <script src="https://cdn.jsdelivr.net/npm/paymob-widget@latest/main.js" type="module"></script>

    <script>
      window.onload = () => {
        new PaymobWidget({
          publicKey: 'egy_pk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
          elementId: 'paymob-widget',
          amount: 100000, // 1,000 EGP
          currency: 'EGP',
          integrationId: 123,
          theme: 'primary',
          customerCanSelect: true,
          onSubmit: (plan) => {
            console.log('Selected plan', plan);
          },
        });
      };
    </script>
  </body>
</html>

Use in a framework (React, Angular, Vue)

The SDK ships as a single self-contained main.js — styles, fonts and icons are bundled in, so there are no extra CSS files to import. Loading the bundle registers the widget on window.PaymobWidget; you then create it against a container element from your component's lifecycle hook.

You can load the bundle in either of two ways:

  • npmnpm install paymob-widget, then import 'paymob-widget'; (a side-effect import that registers window.PaymobWidget).
  • CDN — add <script src="https://cdn.jsdelivr.net/npm/paymob-widget@latest/main.js" type="module"></script> once (e.g. in index.html); no import needed.

The published bundle exposes the class on window (it does not provide a named ESM export), so always construct it as new window.PaymobWidget({...}). Use a unique elementId per instance if you render more than one widget.

TypeScript: add this once (e.g. paymob-widget.d.ts) so window.PaymobWidget is typed:

interface PaymobWidgetPlan {
  id: string | number;
  tenure: number;
  amount: number;
}

interface PaymobWidgetOptions {
  publicKey: string;
  elementId: string;
  amount: number;
  currency?: string;
  integrationId?: number | number[];
  theme?: 'primary' | 'light' | 'dark';
  customerCanSelect?: boolean;
  onSubmit?: (plan?: PaymobWidgetPlan) => void;
}

declare global {
  interface Window {
    PaymobWidget: new (options: PaymobWidgetOptions) => unknown;
  }
}

export {};

React

import { useEffect } from 'react';
import 'paymob-widget'; // registers window.PaymobWidget

export function InstallmentsWidget() {
  useEffect(() => {
    new window.PaymobWidget({
      publicKey: 'egy_pk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
      elementId: 'paymob-widget',
      amount: 100000, // 1,000 EGP
      currency: 'EGP',
      customerCanSelect: true,
      onSubmit: (plan) => console.log('Selected plan', plan),
    });
  }, []);

  return <div id="paymob-widget" />;
}

Angular

Add import 'paymob-widget'; (or the CDN <script> in index.html), then initialise the widget after the view is ready:

import { Component, AfterViewInit } from '@angular/core';
import 'paymob-widget'; // registers window.PaymobWidget

@Component({
  selector: 'app-installments',
  template: '<div id="paymob-widget"></div>',
})
export class InstallmentsComponent implements AfterViewInit {
  ngAfterViewInit(): void {
    new (window as any).PaymobWidget({
      publicKey: 'egy_pk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
      elementId: 'paymob-widget',
      amount: 100000, // 1,000 EGP
      currency: 'EGP',
      customerCanSelect: true,
      onSubmit: (plan: unknown) => console.log('Selected plan', plan),
    });
  }
}

Vue 3

<script setup lang="ts">
import { onMounted } from 'vue';
import 'paymob-widget'; // registers window.PaymobWidget

onMounted(() => {
  new window.PaymobWidget({
    publicKey: 'egy_pk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
    elementId: 'paymob-widget',
    amount: 100000, // 1,000 EGP
    currency: 'EGP',
    customerCanSelect: true,
    onSubmit: (plan) => console.log('Selected plan', plan),
  });
});
</script>

<template>
  <div id="paymob-widget"></div>
</template>

Troubleshooting — "the widget isn't showing"

The widget renders only when all of these are true:

  • amount is a valid positive number (in cents).
  • The plans request to Paymob succeeds (returns a 2xx status).
  • At least one installment plan is available for that amount.

If any of these fail, the widget stays hidden and the reason is logged to the browser console (prefixed with [PaymobWidget]):

| Symptom | Likely cause | | --- | --- | | Nothing renders, console shows an amount error | amount is missing, 0, negative, or not a number. | | Nothing renders, console shows a status code (e.g. 400) | The request to Paymob failed — check your publicKey, integrationId, and currency. | | Request succeeds but nothing renders | No plans are available — e.g. the amount is below the minimum, the integration has no bank installments configured, or the integrationId is invalid. |

Note: These errors are for developers only. Your customers never see error messages on the page or in the dialog.


Good to know

  • Self-contained: everything — CSS, the widget font, and icons — is inlined into main.js. There are no extra files to host or import; the single file is all you ship. (At runtime the widget still calls the Paymob API, and the design system may pull a few supplementary web fonts from Google Fonts, with graceful fallback.)
  • Style isolation: the widget renders inside a Shadow DOM, so its styles won't clash with your page's CSS (and your CSS won't bleed into it).
  • Region: the API region is chosen automatically from your public key's country prefix (egy_, are_, sau_, pak_, omn_).