paymob-widget-alpha 1.0.16 → 1.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +216 -60
- package/main.js +6423 -76492
- package/package.json +1 -1
- package/0-interest.39e7455001b508e7.png +0 -0
- package/Delete-icon.3839812aa3996b863c286b42e3fffa3b.svg +0 -21
- package/Inter-Regular.otf +0 -0
- package/Inter-Regular.ttf +0 -0
- package/Inter-Regular.woff +0 -0
- package/Inter-Regular.woff2 +0 -0
- package/Inter.otf +0 -0
- package/Inter.ttf +0 -0
- package/Inter.woff +0 -0
- package/Inter.woff2 +0 -0
- package/arrow.40b47d938ade37f5820c66b81ae8b353.svg +0 -16
- package/assets/.gitkeep +0 -0
- package/assets/fonts/CurrencyFont/Inter.otf +0 -0
- package/assets/fonts/CurrencyFont/Inter.ttf +0 -0
- package/assets/fonts/CurrencyFont/Inter.woff +0 -0
- package/assets/fonts/CurrencyFont/Inter.woff2 +0 -0
- package/assets/icons/0-interest.png +0 -0
- package/assets/icons/Partner logo.png +0 -0
- package/assets/icons/arrow.png +0 -0
- package/assets/icons/bag.png +0 -0
- package/assets/icons/blue-logo.png +0 -0
- package/assets/icons/calender.png +0 -0
- package/assets/icons/card.png +0 -0
- package/assets/icons/close-arrow.png +0 -0
- package/assets/icons/close.png +0 -0
- package/assets/icons/open-arrow.png +0 -0
- package/assets/icons/paymob-logo.png +0 -0
- package/assets/icons/shop.png +0 -0
- package/authorize-icon.27f6de6230a7258b010095f2ecb62fbb.svg +0 -16
- package/bank-installment.e4813d8ad6f5e1f041f321f3c6dcbb2f.svg +0 -16
- package/card.23ed515bba2fc5ea60108513a05e1ba2.svg +0 -21
- package/check.021d9dd3dee18e90e138da39f88ef694.svg +0 -19
- package/checkGreenIcon.2fdf1421207f99ebe329055b1350a193.svg +0 -16
- package/checkIcon.da837e89923bb68f2c34c3b502b7b3f9.svg +0 -16
- package/chevron-down.413212cab44b2a7ef0f299360be6f2b1.svg +0 -16
- package/circle.22e1a0a2a35306fe278668ddabc49974.svg +0 -16
- package/contact.1ec295a5e0bf9727.png +0 -0
- package/copy.72a7096d2e875cbd4d02df03563ec7c7.svg +0 -16
- package/crypto.3caa56ce2aad387e232e31d62800f356.svg +0 -30
- package/email.928bbae47d30af2076391a2591696665.svg +0 -16
- package/emptyImage.7f00e4ccc5fe1c80425a8d28af3c0e2d.svg +0 -21
- package/error.87f0ee7801122d75f04ce506f360c984.svg +0 -16
- package/expiredIcon.32804bbd5021977df40094b1da6703e7.svg +0 -16
- package/kiosk.1bb782704007efe21b5b71e957b6baee.svg +0 -16
- package/loading-loader.6463e608601cd9d5550f67d4c08d513b.svg +0 -43
- package/madfu.4e378b2568497219.png +0 -0
- package/main.css +0 -4755
- package/mid-takseet.6c95c45e88ec6a5c.png +0 -0
- package/paylater.e1d5f1a0f0d9027330f541ded8752a30.svg +0 -16
- package/payment-succesful-1.dd6ee56033bac452.png +0 -0
- package/payment-succesful-2.a503559a0c9d7a70.png +0 -0
- package/paymentDeclined.b4b9f7ee7680300d.png +0 -0
- package/paymentDeclinedIcon.76cc8bf5bd5f0f9739af86a1b8de62ab.svg +0 -16
- package/paymentpending.ae4dace728429396.png +0 -0
- package/pending-icon.21ad7f408b9b33164ddeab11baa012e5.svg +0 -16
- package/phone.c867b950ae5b9bfd86c96f7a0937d482.svg +0 -16
- package/search-icon.2522b2e216184647659a86a07a383ccd.svg +0 -16
- package/styles.css +0 -253
- package/styles.js +0 -6
- package/wallet.c3700990d3a1ebb570cc2c9f37e07ffc.svg +0 -16
package/README.md
CHANGED
|
@@ -1,108 +1,120 @@
|
|
|
1
|
-
#
|
|
2
|
-
Paymob Installments Widget that can be embedded in any web page to display installment plans and (optionally) let the user select a plan.
|
|
1
|
+
# Paymob Installments Widget
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
`paymob-widget`
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
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.
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
No framework required — it works on any HTML page with a single `<script>` tag,
|
|
8
|
+
and it also drops into React, Angular or Vue apps (see
|
|
9
|
+
[Use in a framework](#use-in-a-framework-react-angular-vue)).
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## How it works
|
|
14
|
+
|
|
15
|
+
1. You add the script and a container element to your page.
|
|
16
|
+
2. You create a `PaymobWidget` with your public key and the order amount.
|
|
17
|
+
3. The widget calls Paymob, fetches the installment plans for that amount, and renders them inside your container.
|
|
18
|
+
|
|
19
|
+
There are two modes:
|
|
20
|
+
|
|
21
|
+
| Mode | What the customer sees | When to use it |
|
|
22
|
+
| --- | --- | --- |
|
|
23
|
+
| **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. |
|
|
24
|
+
| **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. |
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Quick start
|
|
29
|
+
|
|
30
|
+
### 1. Add the script
|
|
31
|
+
|
|
32
|
+
Using the jsDelivr CDN:
|
|
9
33
|
|
|
10
34
|
```html
|
|
11
|
-
<script src="https://cdn.jsdelivr.net/npm/paymob-widget
|
|
35
|
+
<script src="https://cdn.jsdelivr.net/npm/paymob-widget@latest/main.js" type="module"></script>
|
|
12
36
|
```
|
|
13
37
|
|
|
14
|
-
|
|
38
|
+
### 2. Add a container
|
|
15
39
|
|
|
16
|
-
|
|
40
|
+
Place an empty element wherever you want the widget to appear:
|
|
17
41
|
|
|
18
42
|
```html
|
|
19
43
|
<div id="paymob-widget"></div>
|
|
20
44
|
```
|
|
21
45
|
|
|
22
|
-
|
|
46
|
+
### 3. Create the widget
|
|
23
47
|
|
|
24
48
|
```js
|
|
25
49
|
new PaymobWidget({
|
|
26
50
|
publicKey: 'egy_pk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
|
|
27
51
|
elementId: 'paymob-widget',
|
|
28
|
-
amount: 100000, //
|
|
52
|
+
amount: 100000, // order total in cents — this is 1,000 EGP
|
|
29
53
|
currency: 'EGP',
|
|
30
|
-
integrationId: 123 || [123456, 2233], // optional — single number or array
|
|
31
54
|
});
|
|
32
55
|
```
|
|
33
56
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
### When the widget is shown or hidden
|
|
57
|
+
That's it. If installment plans are available for that amount, the widget renders them.
|
|
37
58
|
|
|
38
|
-
|
|
59
|
+
> **⚠️ `amount` is in cents, not whole currency units.**
|
|
60
|
+
> To show plans for **1,000 EGP**, pass `amount: 100000` (1,000 × 100).
|
|
61
|
+
> If `amount` is missing, zero, negative, or not a number, the widget will **not** render and an error is logged to the browser console.
|
|
39
62
|
|
|
40
|
-
|
|
41
|
-
- The installment-plans request returns a **2xx** status
|
|
42
|
-
- At least one installment plan is available after processing the response
|
|
63
|
+
---
|
|
43
64
|
|
|
44
|
-
|
|
65
|
+
## Options
|
|
45
66
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
67
|
+
| Option | Type | Required | Description |
|
|
68
|
+
| --- | --- | --- | --- |
|
|
69
|
+
| `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_`). |
|
|
70
|
+
| `elementId` | `string` | **Yes** | The `id` of the HTML element the widget renders into. |
|
|
71
|
+
| `amount` | `number` | **Yes** | The order total **in cents** (a positive number). No default — the widget won't render without it. |
|
|
72
|
+
| `currency` | `string` | No | Currency code. Default: `"EGP"`. |
|
|
73
|
+
| `integrationId` | `number \| number[]` | No | One or more Paymob integration IDs. When provided, plans are fetched for those integrations only. |
|
|
74
|
+
| `theme` | `"primary" \| "light" \| "dark"` | No | Visual theme. Default: `"primary"`. |
|
|
75
|
+
| `customerCanSelect` | `boolean` | No | Set to `true` to let the customer select a plan and enable **Buy now**. Default: `false` (read-only). |
|
|
76
|
+
| `onSubmit` | `function` | No | Called when the customer clicks **Buy now**. See [Handling the selection](#handling-the-selection). |
|
|
49
77
|
|
|
50
|
-
|
|
78
|
+
---
|
|
51
79
|
|
|
52
|
-
|
|
80
|
+
## Letting customers select a plan
|
|
53
81
|
|
|
54
|
-
|
|
82
|
+
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.
|
|
55
83
|
|
|
56
84
|
```js
|
|
57
85
|
new PaymobWidget({
|
|
58
86
|
publicKey: 'egy_pk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
|
|
59
87
|
elementId: 'paymob-widget',
|
|
60
|
-
amount: 100000, //
|
|
88
|
+
amount: 100000, // 1,000 EGP
|
|
61
89
|
currency: 'EGP',
|
|
62
|
-
integrationId: 123 || [123456, 2233], // optional — single number or array
|
|
63
90
|
theme: 'primary',
|
|
64
91
|
customerCanSelect: true,
|
|
65
92
|
onSubmit: (plan) => {
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
//
|
|
69
|
-
// With customerCanSelect: false, onSubmit is called with no payload.
|
|
93
|
+
// plan = { id: 123, tenure: 12, amount: 25000 }
|
|
94
|
+
console.log('Customer selected plan', plan);
|
|
95
|
+
// → continue your purchase flow here
|
|
70
96
|
},
|
|
71
97
|
});
|
|
72
98
|
```
|
|
73
99
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
| Property | Type | Required | Definition |
|
|
77
|
-
| --- | --- | --- | --- |
|
|
78
|
-
| publicKey | String | Yes | Your public key (used to resolve API base URL and authenticate requests). |
|
|
79
|
-
| elementId | String | Yes | ID of the HTML element where the widget will be rendered. |
|
|
80
|
-
| theme | `"primary" \| "light" \| "dark"` | No | Widget theme. Default: `"primary"`. |
|
|
81
|
-
| amount | Number | Yes | Order total in **cents** (positive finite number). **Required** — no default. The widget will not initialize without it. |
|
|
82
|
-
| currency | String | No | Currency code. Default: `"EGP"`. |
|
|
83
|
-
| integrationId | Number \| Number[] | No | Paymob integration ID(s). If provided, sent when fetching installment plans. |
|
|
84
|
-
| customerCanSelect | Boolean | No | If `true`, the user can select a plan and **Buy now** is enabled. Default: `false`. |
|
|
85
|
-
| onSubmit | Function | No | Called when the user clicks **Buy now**. With `customerCanSelect: true`, receives `{ id, tenure, amount }`. With `false`, called with no payload. |
|
|
86
|
-
|
|
87
|
-
### `customerCanSelect` and Learn more
|
|
88
|
-
|
|
89
|
-
| Value | Behavior |
|
|
90
|
-
| --- | --- |
|
|
91
|
-
| `true` | User can **select** a plan. Learn more includes the **Select Installment Plan** step. |
|
|
92
|
-
| `false` | Plans are **read-only**. Learn more omits the selection step and uses different copy on the final payment step. |
|
|
93
|
-
|
|
94
|
-
### `onSubmit` payload
|
|
100
|
+
### Handling the selection
|
|
95
101
|
|
|
96
|
-
When `customerCanSelect` is `true
|
|
102
|
+
When `customerCanSelect` is `true`, `onSubmit` receives the selected plan:
|
|
97
103
|
|
|
98
104
|
```js
|
|
99
105
|
{
|
|
100
|
-
id: string | number, // installment plan
|
|
106
|
+
id: string | number, // the installment plan ID
|
|
101
107
|
tenure: number, // number of months
|
|
102
|
-
amount: number // monthly installment amount in cents
|
|
108
|
+
amount: number // monthly installment amount, in cents
|
|
103
109
|
}
|
|
104
110
|
```
|
|
105
111
|
|
|
112
|
+
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.
|
|
113
|
+
|
|
114
|
+
The built-in **Learn more** dialog also adapts: with `customerCanSelect: true` it includes a "Select installment plan" step; with `false` it omits that step.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
106
118
|
## Full HTML example
|
|
107
119
|
|
|
108
120
|
```html
|
|
@@ -127,14 +139,14 @@ When `customerCanSelect` is `true`:
|
|
|
127
139
|
<body>
|
|
128
140
|
<div id="paymob-widget"></div>
|
|
129
141
|
|
|
130
|
-
<script src="https://cdn.jsdelivr.net/npm/paymob-widget
|
|
142
|
+
<script src="https://cdn.jsdelivr.net/npm/paymob-widget@latest/main.js" type="module"></script>
|
|
131
143
|
|
|
132
144
|
<script>
|
|
133
145
|
window.onload = () => {
|
|
134
146
|
new PaymobWidget({
|
|
135
147
|
publicKey: 'egy_pk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
|
|
136
148
|
elementId: 'paymob-widget',
|
|
137
|
-
amount: 100000, //
|
|
149
|
+
amount: 100000, // 1,000 EGP
|
|
138
150
|
currency: 'EGP',
|
|
139
151
|
integrationId: 123,
|
|
140
152
|
theme: 'primary',
|
|
@@ -149,7 +161,151 @@ When `customerCanSelect` is `true`:
|
|
|
149
161
|
</html>
|
|
150
162
|
```
|
|
151
163
|
|
|
152
|
-
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Use in a framework (React, Angular, Vue)
|
|
167
|
+
|
|
168
|
+
The SDK ships as a single self-contained `main.js` — styles, fonts and icons are
|
|
169
|
+
bundled in, so there are **no extra CSS files to import**. Loading the bundle
|
|
170
|
+
registers the widget on `window.PaymobWidget`; you then create it against a
|
|
171
|
+
container element from your component's lifecycle hook.
|
|
172
|
+
|
|
173
|
+
You can load the bundle in either of two ways:
|
|
174
|
+
|
|
175
|
+
- **npm** — `npm install paymob-widget`, then `import 'paymob-widget';` (a
|
|
176
|
+
side-effect import that registers `window.PaymobWidget`).
|
|
177
|
+
- **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.
|
|
178
|
+
|
|
179
|
+
> The published bundle exposes the class on `window` (it does **not** provide a
|
|
180
|
+
> named ESM export), so always construct it as `new window.PaymobWidget({...})`.
|
|
181
|
+
> Use a **unique `elementId` per instance** if you render more than one widget.
|
|
182
|
+
|
|
183
|
+
**TypeScript:** add this once (e.g. `paymob-widget.d.ts`) so `window.PaymobWidget` is typed:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
interface PaymobWidgetPlan {
|
|
187
|
+
id: string | number;
|
|
188
|
+
tenure: number;
|
|
189
|
+
amount: number;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
interface PaymobWidgetOptions {
|
|
193
|
+
publicKey: string;
|
|
194
|
+
elementId: string;
|
|
195
|
+
amount: number;
|
|
196
|
+
currency?: string;
|
|
197
|
+
integrationId?: number | number[];
|
|
198
|
+
theme?: 'primary' | 'light' | 'dark';
|
|
199
|
+
customerCanSelect?: boolean;
|
|
200
|
+
onSubmit?: (plan?: PaymobWidgetPlan) => void;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
declare global {
|
|
204
|
+
interface Window {
|
|
205
|
+
PaymobWidget: new (options: PaymobWidgetOptions) => unknown;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export {};
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### React
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
import { useEffect } from 'react';
|
|
216
|
+
import 'paymob-widget'; // registers window.PaymobWidget
|
|
217
|
+
|
|
218
|
+
export function InstallmentsWidget() {
|
|
219
|
+
useEffect(() => {
|
|
220
|
+
new window.PaymobWidget({
|
|
221
|
+
publicKey: 'egy_pk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
|
|
222
|
+
elementId: 'paymob-widget',
|
|
223
|
+
amount: 100000, // 1,000 EGP
|
|
224
|
+
currency: 'EGP',
|
|
225
|
+
customerCanSelect: true,
|
|
226
|
+
onSubmit: (plan) => console.log('Selected plan', plan),
|
|
227
|
+
});
|
|
228
|
+
}, []);
|
|
229
|
+
|
|
230
|
+
return <div id="paymob-widget" />;
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Angular
|
|
235
|
+
|
|
236
|
+
Add `import 'paymob-widget';` (or the CDN `<script>` in `index.html`), then
|
|
237
|
+
initialise the widget after the view is ready:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import { Component, AfterViewInit } from '@angular/core';
|
|
241
|
+
import 'paymob-widget'; // registers window.PaymobWidget
|
|
242
|
+
|
|
243
|
+
@Component({
|
|
244
|
+
selector: 'app-installments',
|
|
245
|
+
template: '<div id="paymob-widget"></div>',
|
|
246
|
+
})
|
|
247
|
+
export class InstallmentsComponent implements AfterViewInit {
|
|
248
|
+
ngAfterViewInit(): void {
|
|
249
|
+
new (window as any).PaymobWidget({
|
|
250
|
+
publicKey: 'egy_pk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
|
|
251
|
+
elementId: 'paymob-widget',
|
|
252
|
+
amount: 100000, // 1,000 EGP
|
|
253
|
+
currency: 'EGP',
|
|
254
|
+
customerCanSelect: true,
|
|
255
|
+
onSubmit: (plan: unknown) => console.log('Selected plan', plan),
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Vue 3
|
|
262
|
+
|
|
263
|
+
```vue
|
|
264
|
+
<script setup lang="ts">
|
|
265
|
+
import { onMounted } from 'vue';
|
|
266
|
+
import 'paymob-widget'; // registers window.PaymobWidget
|
|
267
|
+
|
|
268
|
+
onMounted(() => {
|
|
269
|
+
new window.PaymobWidget({
|
|
270
|
+
publicKey: 'egy_pk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
|
|
271
|
+
elementId: 'paymob-widget',
|
|
272
|
+
amount: 100000, // 1,000 EGP
|
|
273
|
+
currency: 'EGP',
|
|
274
|
+
customerCanSelect: true,
|
|
275
|
+
onSubmit: (plan) => console.log('Selected plan', plan),
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
</script>
|
|
279
|
+
|
|
280
|
+
<template>
|
|
281
|
+
<div id="paymob-widget"></div>
|
|
282
|
+
</template>
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Troubleshooting — "the widget isn't showing"
|
|
288
|
+
|
|
289
|
+
The widget renders **only** when all of these are true:
|
|
290
|
+
|
|
291
|
+
- `amount` is a valid positive number (in cents).
|
|
292
|
+
- The plans request to Paymob succeeds (returns a `2xx` status).
|
|
293
|
+
- At least one installment plan is available for that amount.
|
|
294
|
+
|
|
295
|
+
If any of these fail, the widget stays hidden and the reason is logged to the **browser console** (prefixed with `[PaymobWidget]`):
|
|
296
|
+
|
|
297
|
+
| Symptom | Likely cause |
|
|
298
|
+
| --- | --- |
|
|
299
|
+
| Nothing renders, console shows an `amount` error | `amount` is missing, `0`, negative, or not a number. |
|
|
300
|
+
| Nothing renders, console shows a status code (e.g. `400`) | The request to Paymob failed — check your `publicKey`, `integrationId`, and `currency`. |
|
|
301
|
+
| 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. |
|
|
302
|
+
|
|
303
|
+
> **Note:** These errors are for developers only. Your customers never see error messages on the page or in the dialog.
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## Good to know
|
|
153
308
|
|
|
154
|
-
- **
|
|
155
|
-
- **
|
|
309
|
+
- **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.)
|
|
310
|
+
- **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).
|
|
311
|
+
- **Region:** the API region is chosen automatically from your public key's country prefix (`egy_`, `are_`, `sau_`, `pak_`, `omn_`).
|