@thepayulink/checkout 1.0.0 → 2.0.1
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 +63 -73
- package/dist/index.d.ts +36 -41
- package/dist/payulink-checkout.cjs +42 -334
- package/dist/payulink-checkout.esm.js +42 -334
- package/dist/payulink-checkout.umd.js +42 -334
- package/package.json +7 -10
- package/src/index.js +130 -94
- package/src/overlay.js +141 -0
- package/src/modal.js +0 -362
- package/src/style.js +0 -251
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @thepayulink/checkout
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Browser checkout for the [PayuLink](https://payulink.io) Checkout API. Open an
|
|
4
|
+
order your server created, using only your **publishable key**. If you've used
|
|
5
|
+
Razorpay's `checkout.js`, this will feel familiar.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -10,97 +10,87 @@ A drop-in **UPI payment checkout** for Elite Yatra — the same Razorpay-style m
|
|
|
10
10
|
npm install @thepayulink/checkout
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
Or
|
|
13
|
+
Or with no build step, from the CDN:
|
|
14
14
|
|
|
15
15
|
```html
|
|
16
|
-
|
|
16
|
+
<!-- pinned (recommended: immutable, cached a year) -->
|
|
17
|
+
<script src="https://assetcdn.payulink.io/checkout/v1/checkout-2.0.0.js"></script>
|
|
18
|
+
|
|
19
|
+
<!-- or float on the latest v1 (cached 5 min) -->
|
|
20
|
+
<script src="https://assetcdn.payulink.io/checkout/v1/checkout.js"></script>
|
|
17
21
|
```
|
|
18
22
|
|
|
19
23
|
## Quick start
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
Your backend creates the order with
|
|
26
|
+
[`@thepayulink/server`](https://www.npmjs.com/package/@thepayulink/server) and
|
|
27
|
+
hands the client an `order_id`:
|
|
22
28
|
|
|
23
29
|
```js
|
|
24
|
-
import
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// Fired once the server confirms the payment (webhook-verified).
|
|
39
|
-
console.log('Paid!', res.merchant_order_no, res.utr);
|
|
30
|
+
import PayuLink from '@thepayulink/checkout';
|
|
31
|
+
|
|
32
|
+
const pl = new PayuLink({ key: 'pl_live_…' }); // publishable key ONLY
|
|
33
|
+
|
|
34
|
+
pl.open({
|
|
35
|
+
order_id: orderId,
|
|
36
|
+
prefill: { name: 'A Kumar', contact: '9876543210' },
|
|
37
|
+
handler: async (r) => {
|
|
38
|
+
// Send the handoff to YOUR server and verify it there before delivering anything.
|
|
39
|
+
await fetch('/verify', {
|
|
40
|
+
method: 'POST',
|
|
41
|
+
headers: { 'Content-Type': 'application/json' },
|
|
42
|
+
body: JSON.stringify(r), // payulink_order_id / payulink_payment_id / payulink_signature
|
|
43
|
+
});
|
|
40
44
|
},
|
|
41
|
-
onDismiss: () =>
|
|
45
|
+
onDismiss: () => {},
|
|
42
46
|
});
|
|
43
47
|
```
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
```html
|
|
48
|
-
<script src="https://eliteyatra.vip/sdk/v1/checkout.js"></script>
|
|
49
|
-
<script>
|
|
50
|
-
new PayulinkCheckout({ apiBase: 'https://eliteyatra.vip' })
|
|
51
|
-
.open({ amount: 19900, item: 'Tour booking', handler: (r) => alert(r.merchant_order_no) });
|
|
52
|
-
</script>
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## How it works
|
|
56
|
-
|
|
57
|
-
1. `open()` renders the modal and (once contact details are known) calls **`POST {apiBase}/api/checkout`** to create a real PayuLink PayIn order.
|
|
58
|
-
2. It shows the UPI QR + intent links and streams live status over **`GET {apiBase}/api/order/:id/events`** (Server-Sent Events), falling back to polling.
|
|
59
|
-
3. When the payment is verified by PayuLink's webhook, the modal shows the success screen and your `handler` fires.
|
|
60
|
-
|
|
61
|
-
Your server (`eliteyatra_pay`) is the source of truth — the SDK never sees PayuLink secrets.
|
|
49
|
+
Via `<script>`, the same class is available as the global `PayuLink`.
|
|
62
50
|
|
|
63
51
|
## Options
|
|
64
52
|
|
|
65
|
-
|
|
53
|
+
| Option | Type | Description |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| `key` | string | **Required.** Publishable `pl_live_…` / `pl_test_…` key. |
|
|
56
|
+
| `order_id` | string | **Required.** From `POST /v1/orders` on your server. |
|
|
57
|
+
| `prefill` | object | `{ name, contact, email }` — saves the customer typing. |
|
|
58
|
+
| `handler` | function | Called on success with the signed handoff. |
|
|
59
|
+
| `onDismiss` | function | Called if the customer closes without paying. |
|
|
60
|
+
| `onError` | function | Called on failure. |
|
|
61
|
+
| `theme` | object | `{ color: '#38BDF8' }` |
|
|
62
|
+
| `apiBase` | string | Default `https://payulink.io/api`. |
|
|
66
63
|
|
|
67
|
-
|
|
68
|
-
| --- | --- | --- | --- |
|
|
69
|
-
| `apiBase` | string | `https://eliteyatra.vip` | Elite Yatra pay server base URL. |
|
|
70
|
-
| `assetBase` | string | `${apiBase}/assets` | Where `/upi/*.png` and `/payulink-logo.png` are served. |
|
|
71
|
-
| `name` | string | `Elite Yatra` | Business name on the left panel. |
|
|
72
|
-
| `logo` | string | payulink logo | Left-panel logo URL. |
|
|
73
|
-
| `brandColor` | string | `#0b3068` | Left-panel gradient accent. |
|
|
74
|
-
| `amount` | number | — | **Required.** Amount in rupees. |
|
|
75
|
-
| `item` | string | `name` | What the customer is paying for. |
|
|
76
|
-
| `note` | string | — | Sub-line under the price summary. |
|
|
77
|
-
| `prefill` | object | `{}` | `{ name, contact, email }`. If `name` + 10-digit `contact` are present, the contact form is skipped. |
|
|
78
|
-
| `handler` | function | — | `({ merchant_order_no, utr, amount }) => void` on success. |
|
|
79
|
-
| `onDismiss` | function | — | Called when the modal is closed without paying. |
|
|
80
|
-
| `key` | string | — | Optional public key, sent as the `x-payulink-key` header. |
|
|
81
|
-
|
|
82
|
-
`open()` returns `{ close() }` so you can dismiss the modal programmatically.
|
|
83
|
-
|
|
84
|
-
## React Native
|
|
64
|
+
## How it works
|
|
85
65
|
|
|
86
|
-
|
|
66
|
+
1. `open()` calls **`POST /v1/checkout/session`** with your `key_id` + `order_id`.
|
|
67
|
+
2. PayuLink verifies the order belongs to you and returns the hosted payment page.
|
|
68
|
+
**The amount comes from the stored order** — the browser cannot change it.
|
|
69
|
+
3. The customer pays by UPI. The hosted page handles the QR and auto-verification.
|
|
70
|
+
4. Once PayuLink verifies the payment, your `handler` fires with the signed handoff.
|
|
87
71
|
|
|
88
|
-
|
|
89
|
-
import { WebView } from 'react-native-webview';
|
|
72
|
+
## Security
|
|
90
73
|
|
|
91
|
-
|
|
92
|
-
|
|
74
|
+
- **Never put a `key_secret` here.** This SDK throws if you pass one. Only the
|
|
75
|
+
publishable `key_id` belongs in client code.
|
|
76
|
+
- **There is no client-supplied amount.** The client can only reference an order
|
|
77
|
+
your server already created and priced.
|
|
78
|
+
- **`handler` firing is not proof of payment** — it's a browser claiming success.
|
|
79
|
+
Verify `payulink_signature` on your server with
|
|
80
|
+
`@thepayulink/server` → `verifyPaymentSignature()`, or act on the webhook.
|
|
81
|
+
Webhooks are the record that survives the customer closing the tab.
|
|
93
82
|
|
|
94
|
-
##
|
|
83
|
+
## Upgrading from 1.x
|
|
95
84
|
|
|
96
|
-
|
|
85
|
+
1.x talked to a self-hosted proxy and let the client pass an `amount`. 2.x talks
|
|
86
|
+
to PayuLink directly and removes that.
|
|
97
87
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
88
|
+
| 1.x | 2.x |
|
|
89
|
+
| --- | --- |
|
|
90
|
+
| `new PayulinkCheckout({ apiBase })` | `new PayuLink({ key })` |
|
|
91
|
+
| `open({ amount, item })` | `open({ order_id })` — priced server-side |
|
|
92
|
+
| `handler({ merchant_order_no, utr })` | `handler({ payulink_order_id, payulink_payment_id, payulink_signature })` |
|
|
103
93
|
|
|
104
|
-
|
|
94
|
+
`window.PayulinkCheckout` remains aliased for existing `<script>` embeds.
|
|
105
95
|
|
|
106
|
-
|
|
96
|
+
Full guide: <https://payulink.io> · License: MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -1,66 +1,61 @@
|
|
|
1
|
-
|
|
1
|
+
// Type definitions for @thepayulink/checkout
|
|
2
|
+
|
|
3
|
+
export interface PayuLinkPrefill {
|
|
2
4
|
name?: string;
|
|
3
5
|
/** 10-digit Indian mobile number. */
|
|
4
6
|
contact?: string;
|
|
5
7
|
email?: string;
|
|
6
8
|
}
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
/** The signed handoff. Send this to YOUR server and verify it there. */
|
|
11
|
+
export interface PayuLinkSuccess {
|
|
12
|
+
payulink_order_id: string;
|
|
13
|
+
payulink_payment_id: string;
|
|
14
|
+
/** HMAC_SHA256(order_id + "|" + payment_id, key_secret) — verify server-side. */
|
|
15
|
+
payulink_signature: string;
|
|
16
|
+
utr?: string | null;
|
|
17
|
+
/** PAISE */
|
|
18
|
+
amount?: number;
|
|
12
19
|
}
|
|
13
20
|
|
|
14
|
-
export interface
|
|
15
|
-
/**
|
|
21
|
+
export interface PayuLinkOptions {
|
|
22
|
+
/** Your PUBLISHABLE key: pl_live_… / pl_test_…. Never a key_secret. */
|
|
23
|
+
key: string;
|
|
24
|
+
/** Gateway base URL. Default https://payulink.io/api */
|
|
16
25
|
apiBase?: string;
|
|
17
|
-
/**
|
|
18
|
-
assetBase?: string;
|
|
19
|
-
/** Business name shown on the left panel. */
|
|
26
|
+
/** Overrides the merchant name shown in the header. */
|
|
20
27
|
name?: string;
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
/** Optional public key, sent as the `x-payulink-key` request header. */
|
|
26
|
-
key?: string;
|
|
28
|
+
/** Accent colour, e.g. { color: '#38BDF8' } */
|
|
29
|
+
theme?: { color?: string };
|
|
30
|
+
/** Status poll interval in ms. Default 2500. */
|
|
31
|
+
pollMs?: number;
|
|
27
32
|
}
|
|
28
33
|
|
|
29
|
-
export interface
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
/** Amount in rupees. Required unless `orderId` is given. */
|
|
37
|
-
amount?: number;
|
|
38
|
-
/** What the customer is paying for. */
|
|
39
|
-
item?: string;
|
|
40
|
-
/** Optional sub-line under the price summary. */
|
|
41
|
-
note?: string;
|
|
42
|
-
prefill?: PayulinkPrefill;
|
|
43
|
-
/** Called on a successful payment. */
|
|
44
|
-
handler?: (result: PayulinkSuccess) => void;
|
|
45
|
-
/** Called when the modal is dismissed without paying. */
|
|
34
|
+
export interface PayuLinkOpenOptions extends Partial<PayuLinkOptions> {
|
|
35
|
+
/** Required. Created by YOUR server via POST /v1/orders. */
|
|
36
|
+
order_id: string;
|
|
37
|
+
prefill?: PayuLinkPrefill;
|
|
38
|
+
/** Called on success with the signed handoff. */
|
|
39
|
+
handler?: (result: PayuLinkSuccess) => void;
|
|
40
|
+
/** Called if the customer closes the checkout without paying. */
|
|
46
41
|
onDismiss?: () => void;
|
|
47
|
-
/** Called if the order can't be opened (e.g. unknown/expired orderId). */
|
|
48
42
|
onError?: (error: Error) => void;
|
|
49
43
|
}
|
|
50
44
|
|
|
51
|
-
export interface
|
|
52
|
-
/** Programmatically close the modal. */
|
|
45
|
+
export interface PayuLinkHandle {
|
|
53
46
|
close(): void;
|
|
54
47
|
}
|
|
55
48
|
|
|
56
|
-
export default class
|
|
57
|
-
constructor(options
|
|
58
|
-
open(
|
|
59
|
-
static open(options:
|
|
49
|
+
export default class PayuLink {
|
|
50
|
+
constructor(options: PayuLinkOptions);
|
|
51
|
+
open(options: PayuLinkOpenOptions): PayuLinkHandle;
|
|
52
|
+
static open(options: PayuLinkOptions & PayuLinkOpenOptions): PayuLinkHandle;
|
|
60
53
|
}
|
|
61
54
|
|
|
62
55
|
declare global {
|
|
63
56
|
interface Window {
|
|
64
|
-
|
|
57
|
+
PayuLink: typeof PayuLink;
|
|
58
|
+
/** Back-compat alias for pre-2.x <script> embeds. */
|
|
59
|
+
PayulinkCheckout: typeof PayuLink;
|
|
65
60
|
}
|
|
66
61
|
}
|