@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 CHANGED
@@ -1,8 +1,8 @@
1
1
  # @thepayulink/checkout
2
2
 
3
- A drop-in **UPI payment checkout** for Elite Yatra — the same Razorpay-style modal used on eliteyatra.vip, packaged so any website or web app can open it in one call. Payments run through the Elite Yatra pay server and settle to Elite Yatra's PayuLink account.
4
-
5
- ![Payment Options modal](https://eliteyatra.vip/assets/payulink-logo.png)
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 load it straight from the Elite Yatra server / a CDN — no build step:
13
+ Or with no build step, from the CDN:
14
14
 
15
15
  ```html
16
- <script src="https://eliteyatra.vip/sdk/v1/checkout.js"></script>
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
- ### With a bundler (React, Vue, Vite, Next.js, plain ESM)
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 PayulinkCheckout from '@thepayulink/checkout';
25
-
26
- const checkout = new PayulinkCheckout({
27
- apiBase: 'https://eliteyatra.vip', // your Elite Yatra pay server
28
- name: 'Elite Yatra',
29
- logo: 'https://eliteyatra.vip/brand-logo.png',
30
- brandColor: '#0b3068',
31
- });
32
-
33
- checkout.open({
34
- amount: 19900, // rupees
35
- item: 'Kailash Mansarovar Yatra — 2 travellers',
36
- prefill: { name: 'Asha', contact: '9876543210', email: 'asha@example.com' },
37
- handler: (res) => {
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: () => console.log('closed without paying'),
45
+ onDismiss: () => {},
42
46
  });
43
47
  ```
44
48
 
45
- ### With a plain `<script>` tag
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
- Constructor options (shared defaults) and `open()` options are merged; anything valid in one works in the other.
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
- | Option | Type | Default | Description |
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
- This package renders a DOM modal and runs in **browsers only**. In a React Native app, use a `WebView` pointed at the hosted checkout page instead:
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
- ```jsx
89
- import { WebView } from 'react-native-webview';
72
+ ## Security
90
73
 
91
- <WebView source={{ uri: 'https://eliteyatra.vip/pay?amount=19900&item=Tour%20booking&name=Asha&phone=9876543210' }} />
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
- ## Server requirements
83
+ ## Upgrading from 1.x
95
84
 
96
- The `apiBase` server must expose (already implemented in `eliteyatra_pay/server.js`):
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
- - `POST /api/checkout` `{ merchant_order_no, amount, qr_data_url, upi_intent_url, payment_link, expires_at, server_now }`
99
- - `GET /api/order/:id` order status
100
- - `GET /api/order/:id/events` SSE status stream
101
- - Static `/assets/upi/*.png` and `/assets/payulink-logo.png`
102
- - **CORS** allowing the embedding origin (see `server.js`).
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
- ## License
94
+ `window.PayulinkCheckout` remains aliased for existing `<script>` embeds.
105
95
 
106
- UNLICENSED internal to Elite Yatra.
96
+ Full guide: <https://payulink.io> · License: MIT
package/dist/index.d.ts CHANGED
@@ -1,66 +1,61 @@
1
- export interface PayulinkPrefill {
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
- export interface PayulinkSuccess {
9
- merchant_order_no: string;
10
- utr: string | null;
11
- amount: number;
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 PayulinkOptions {
15
- /** Elite Yatra pay server base URL. Default: https://eliteyatra.vip */
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
- /** Base URL serving /upi/*.png and /payulink-logo.png. Default: `${apiBase}/assets`. */
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
- /** Logo URL for the left panel. */
22
- logo?: string;
23
- /** Accent color for the left panel gradient, e.g. '#0b3068'. */
24
- brandColor?: string;
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 PayulinkPayment extends PayulinkOptions {
30
- /**
31
- * SECURE flow: id of an order created by your backend via
32
- * `POST /api/orders` (secret key). The amount is fixed server-side; when set,
33
- * `amount` is ignored.
34
- */
35
- orderId?: string;
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 PayulinkInstance {
52
- /** Programmatically close the modal. */
45
+ export interface PayuLinkHandle {
53
46
  close(): void;
54
47
  }
55
48
 
56
- export default class PayulinkCheckout {
57
- constructor(options?: PayulinkOptions);
58
- open(payment: PayulinkPayment): PayulinkInstance;
59
- static open(options: PayulinkPayment): PayulinkInstance;
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
- PayulinkCheckout: typeof PayulinkCheckout;
57
+ PayuLink: typeof PayuLink;
58
+ /** Back-compat alias for pre-2.x <script> embeds. */
59
+ PayulinkCheckout: typeof PayuLink;
65
60
  }
66
61
  }