@thepayulink/checkout 2.1.4 → 2.2.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 +13 -4
- package/dist/payulink-checkout.cjs +41 -30
- package/dist/payulink-checkout.esm.js +42 -31
- package/dist/payulink-checkout.umd.js +41 -30
- package/package.json +1 -1
- package/src/index.js +28 -3
- package/src/modal.js +31 -3
- package/src/style.js +9 -0
package/README.md
CHANGED
|
@@ -14,9 +14,9 @@ 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.
|
|
17
|
+
<script src="https://assetcdn.payulink.io/checkout/v1/checkout-2.2.1.js"></script>
|
|
18
18
|
|
|
19
|
-
<!-- or float on the latest v1 (cached
|
|
19
|
+
<!-- or float on the latest v1 (cached up to 4 hours) -->
|
|
20
20
|
<script src="https://assetcdn.payulink.io/checkout/v1/checkout.js"></script>
|
|
21
21
|
```
|
|
22
22
|
|
|
@@ -58,7 +58,7 @@ Via `<script>`, the same class is available as the global `PayuLink`.
|
|
|
58
58
|
| `handler` | function | Called on success with the signed handoff. |
|
|
59
59
|
| `onDismiss` | function | Called if the customer closes without paying. |
|
|
60
60
|
| `onError` | function | Called on failure. |
|
|
61
|
-
| `theme` | object | `{ color: '#38BDF8' }` |
|
|
61
|
+
| `theme` | object | `{ color: '#38BDF8' }` — colour of the brand panel (2.2.0+). |
|
|
62
62
|
| `apiBase` | string | Default `https://payulink.io/api`. |
|
|
63
63
|
|
|
64
64
|
## How it works
|
|
@@ -66,9 +66,18 @@ Via `<script>`, the same class is available as the global `PayuLink`.
|
|
|
66
66
|
1. `open()` calls **`POST /v1/checkout/session`** with your `key_id` + `order_id`.
|
|
67
67
|
2. PayuLink verifies the order belongs to you and returns the hosted payment page.
|
|
68
68
|
**The amount comes from the stored order** — the browser cannot change it.
|
|
69
|
-
3. The customer pays by UPI
|
|
69
|
+
3. The customer pays by UPI — the modal shows the tracked QR PayuLink issued for the order,
|
|
70
|
+
and PayuLink verifies the payment automatically.
|
|
70
71
|
4. Once PayuLink verifies the payment, your `handler` fires with the signed handoff.
|
|
71
72
|
|
|
73
|
+
## Test mode
|
|
74
|
+
|
|
75
|
+
With a `pl_test_` key (on an order your server created with the matching test key), the
|
|
76
|
+
checkout shows a **TEST** badge and **Simulate success** / **Simulate failure** buttons
|
|
77
|
+
instead of a real UPI QR. No real money can move. A simulated success still gives
|
|
78
|
+
`handler` a signed handoff and sends `payin.verified` with `livemode: false`, so your
|
|
79
|
+
whole flow is exercised. Needs 2.2.0 or later.
|
|
80
|
+
|
|
72
81
|
## Security
|
|
73
82
|
|
|
74
83
|
- **Never put a `key_secret` here.** This SDK throws if you pass one. Only the
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
1
|
+
var O=Object.defineProperty;var le=Object.getOwnPropertyDescriptor;var ae=Object.getOwnPropertyNames;var te=Object.prototype.hasOwnProperty;var re=(l,i)=>{for(var r in i)O(l,r,{get:i[r],enumerable:!0})},ie=(l,i,r,m)=>{if(i&&typeof i=="object"||typeof i=="function")for(let h of ae(i))!te.call(l,h)&&h!==r&&O(l,h,{get:()=>i[h],enumerable:!(m=le(i,h))||m.enumerable});return l};var oe=l=>ie(O({},"__esModule",{value:!0}),l);var ce={};re(ce,{default:()=>M});module.exports=oe(ce);var ne=`
|
|
2
2
|
#pl-overlay * { box-sizing: border-box; margin: 0; padding: 0; }
|
|
3
3
|
#pl-overlay { --pl-blue:#3395ff; --pl-blue-dark:#1f86f5; --pl-brand:#0b3068; --pl-ink:#1a2a44; --pl-mut:#6b7c93; --pl-line:#eceff4;
|
|
4
4
|
position: fixed; inset: 0; z-index: 2147483000; font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
|
|
@@ -174,6 +174,15 @@ var E=Object.defineProperty;var ee=Object.getOwnPropertyDescriptor;var le=Object
|
|
|
174
174
|
#pl-overlay .pl-vparow { display: flex; flex-direction: column; gap: 12px; border: 1px solid #e6ebf4; border-radius: 14px; padding: 16px; }
|
|
175
175
|
#pl-overlay .pl-vpa { width: 100%; padding: 14px 14px; border: 1px solid #d5dae0; border-radius: 10px; font-size: 15px; font-family: inherit; color: var(--pl-ink); outline: none; transition: border-color .15s, box-shadow .15s; }
|
|
176
176
|
#pl-overlay .pl-vpa:focus { border-color: var(--pl-blue); box-shadow: 0 0 0 3px rgba(51,149,255,.16); }
|
|
177
|
+
#pl-overlay .pl-testbadge { font-size: 10.5px; font-weight: 800; letter-spacing: .6px; color: #92400e; background: #fef3c7; border: 1px solid #f59e0b; padding: 3px 8px; border-radius: 999px; flex-shrink: 0; }
|
|
178
|
+
#pl-overlay .pl-testnote { font-size: 11px; font-weight: 800; letter-spacing: .5px; color: #92400e; background: #fef3c7; border: 1px dashed #f59e0b; padding: 5px 8px; border-radius: 8px; margin-bottom: 10px; text-align: center; }
|
|
179
|
+
#pl-overlay .pl-simrow { display: flex; gap: 10px; }
|
|
180
|
+
#pl-overlay .pl-sim { flex: 1; padding: 13px 10px; border-radius: 10px; font-size: 14px; font-weight: 700; font-family: inherit; cursor: pointer; border: 1px solid transparent; transition: filter .15s, opacity .15s; }
|
|
181
|
+
#pl-overlay .pl-sim:disabled { opacity: .55; cursor: default; }
|
|
182
|
+
#pl-overlay .pl-sim-ok { background: #16a34a; color: #fff; }
|
|
183
|
+
#pl-overlay .pl-sim-fail { background: #fff; color: #b91c1c; border-color: #fca5a5; }
|
|
184
|
+
#pl-overlay .pl-sim:not(:disabled):hover { filter: brightness(.95); }
|
|
185
|
+
@media (max-width: 840px) { #pl-overlay .pl-testnote { display: none; } }
|
|
177
186
|
#pl-overlay .pl-vpa::placeholder { color: #aab2bf; }
|
|
178
187
|
#pl-overlay .pl-verify { width: 100%; padding: 15px; background: #16181f; color: #fff; border: none; border-radius: 10px; font-size: 15px; font-weight: 700; cursor: pointer; font-family: inherit; transition: filter .15s, transform .08s; }
|
|
179
188
|
#pl-overlay .pl-verify:hover { filter: brightness(1.25); }
|
|
@@ -237,7 +246,7 @@ var E=Object.defineProperty;var ee=Object.getOwnPropertyDescriptor;var le=Object
|
|
|
237
246
|
#pl-overlay .pl-approw { justify-content: center; }
|
|
238
247
|
#pl-overlay .pl-validrow { margin: 0 auto; }
|
|
239
248
|
}
|
|
240
|
-
`,
|
|
249
|
+
`,D=!1;function R(l=document){if(D&&l.getElementById("pl-checkout-styles"))return;let i=l.createElement("style");i.id="pl-checkout-styles",i.textContent=ne,l.head.appendChild(i),D=!0}var pe=l=>{let i=Number(l||0);return"\u20B9"+i.toLocaleString("en-IN",{minimumFractionDigits:i%1===0?0:2,maximumFractionDigits:2})},se=l=>`${Math.floor(l/60)}:${l%60<10?"0":""}${l%60}`,y=l=>String(l==null?"":l).replace(/[&<>"]/g,i=>({"&":"&","<":"<",">":">",'"':"""})[i]),x=l=>`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${l}</svg>`,S={qr:x('<rect width="5" height="5" x="3" y="3" rx="1"/><rect width="5" height="5" x="16" y="3" rx="1"/><rect width="5" height="5" x="3" y="16" rx="1"/><path d="M21 16h-3a2 2 0 0 0-2 2v3"/><path d="M21 21v.01"/><path d="M12 7v3a2 2 0 0 1-2 2H7"/><path d="M3 12h.01"/><path d="M12 3h.01"/><path d="M12 16v.01"/><path d="M16 12h1"/><path d="M21 12v.01"/><path d="M12 21v-1"/>'),card:x('<rect width="20" height="14" x="2" y="5" rx="2"/><line x1="2" x2="22" y1="10" y2="10"/>'),netbanking:x('<path d="M10 18v-7"/><path d="M11.119 2.205a2 2 0 0 1 1.762 0l7.84 3.846A.5.5 0 0 1 20.5 7h-17a.5.5 0 0 1-.22-.949z"/><path d="M14 18v-7"/><path d="M18 18v-7"/><path d="M3 22h18"/><path d="M6 18v-7"/>'),wallet:x('<path d="M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1"/><path d="M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4"/>'),emi:x('<path d="M16 14v2.2l1.6 1"/><path d="M16 2v4"/><path d="M21 7.5V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h3.5"/><path d="M3 10h5"/><path d="M8 2v4"/><circle cx="16" cy="16" r="6"/>'),back:x('<path d="M15 18l-6-6 6-6"/>'),mail:x('<rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-10 5L2 7"/>'),arrow:x('<path d="M5 12h14M13 6l6 6-6 6"/>'),verified:x('<path d="M9 12l2 2 4-4"/><circle cx="12" cy="12" r="10"/>'),lock:x('<rect width="18" height="11" x="3" y="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>')},N='<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M12 1l9 4v6c0 5.5-3.8 10.7-9 12-5.2-1.3-9-6.5-9-12V5l9-4z"/></svg>',de=[{m:"card",t:"Cards",s:"Visa, Mastercard & RuPay",ic:"card"},{m:"netbanking",t:"Netbanking",s:"All Indian banks",ic:"netbanking"},{m:"wallet",t:"Wallet",s:"Paytm, PhonePe & more",ic:"wallet"},{m:"emi",t:"EMI",s:"EMI via cards & more",ic:"emi"}];function H(l){R(),l=Object.assign({name:"Payment",description:"",amount:0,assetBase:"/assets",logo:"/assets/payulink-logo.png",prefill:{},apiBase:""},l);let i=String(l.apiBase||"").replace(/\/$/,""),r=pe(l.amount),m=["gpay","phonepe","paytm","bhim","amazonpay"].reduce((e,a)=>(e[a]=`${l.assetBase}/upi/${a}.png`,e),{}),h=["phonepe","gpay","paytm","bhim","amazonpay"],t={screen:"loader",order:null,customer:null,timer:null,stop:null,done:!1,utr:null,expiryMs:null},o=document.createElement("div");o.id="pl-overlay",l.brandColor&&o.style.setProperty("--pl-brand",l.brandColor),o.innerHTML=`
|
|
241
250
|
<div class="pl-modal" role="dialog" aria-modal="true">
|
|
242
251
|
<div class="pl-splash" id="pl-splash">
|
|
243
252
|
<div class="pl-shield">
|
|
@@ -246,87 +255,89 @@ var E=Object.defineProperty;var ee=Object.getOwnPropertyDescriptor;var le=Object
|
|
|
246
255
|
<path d="M60 3 L113 25 V70 C113 105 90 128 60 137 C30 128 7 105 7 70 V25 Z" fill="url(#pl-shgrad)"/>
|
|
247
256
|
</svg>
|
|
248
257
|
<span class="pl-shield-shine"></span>
|
|
249
|
-
<img class="pl-shield-logo" src="${
|
|
258
|
+
<img class="pl-shield-logo" src="${y(l.assetBase)}/payulink-logo.png" alt="payulink">
|
|
250
259
|
</div>
|
|
251
|
-
<div class="pl-splash-foot">Secured By <span class="pl-pb"><img src="${
|
|
260
|
+
<div class="pl-splash-foot">Secured By <span class="pl-pb"><img src="${y(l.assetBase)}/payulink-logo.png" alt=""><strong>payulink</strong></span></div>
|
|
252
261
|
</div>
|
|
253
262
|
<aside class="pl-left">
|
|
254
263
|
<div class="pl-brandrow">
|
|
255
|
-
<div class="pl-logo"><img src="${
|
|
264
|
+
<div class="pl-logo"><img src="${y(l.logo)}" alt=""></div>
|
|
256
265
|
<div class="pl-bmeta">
|
|
257
|
-
<div class="pl-bname">${
|
|
258
|
-
<div class="pl-btrust">${
|
|
266
|
+
<div class="pl-bname">${y(l.name)}</div>
|
|
267
|
+
<div class="pl-btrust">${S.verified}<span>Trusted Business</span></div>
|
|
259
268
|
</div>
|
|
260
269
|
</div>
|
|
261
270
|
<div class="pl-summary">
|
|
271
|
+
${l.testMode?'<div class="pl-testnote">TEST MODE \xB7 no real payment</div>':""}
|
|
262
272
|
<div class="pl-sl">Price Summary</div>
|
|
263
273
|
<div class="pl-samt">${r}</div>
|
|
264
|
-
<div class="pl-snote">${
|
|
274
|
+
<div class="pl-snote">${y(l.note||"Paying for "+(l.description||l.name))}</div>
|
|
265
275
|
</div>
|
|
266
276
|
<svg class="pl-illus" viewBox="0 0 230 150" fill="none"><rect x="20" y="70" width="58" height="68" rx="5" fill="#fff" fill-opacity=".92"/><path d="M33 70c0-10 5-16 16-16s16 6 16 16" stroke="#fff" stroke-width="3.5"/><rect x="88" y="55" width="68" height="83" rx="5" fill="#fff"/><path d="M104 55c0-12 6-19 18-19s18 7 18 19" stroke="#fff" stroke-width="3.5"/><rect x="150" y="92" width="52" height="46" rx="4" fill="#fff" fill-opacity=".92"/><circle cx="178" cy="40" r="18" fill="#fff"/><path d="M178 30v20M168 40h20" stroke="currentColor" stroke-width="3.5" opacity=".5"/></svg>
|
|
267
|
-
<div class="pl-secured">${
|
|
277
|
+
<div class="pl-secured">${N}Secured by <span class="pl-pb"><img src="${y(l.assetBase)}/payulink-logo.png" alt=""><strong>payulink</strong></span></div>
|
|
268
278
|
</aside>
|
|
269
279
|
<section class="pl-main">
|
|
270
280
|
<header class="pl-head">
|
|
271
|
-
<button class="pl-hic pl-back" id="pl-back" aria-label="Back">${
|
|
281
|
+
<button class="pl-hic pl-back" id="pl-back" aria-label="Back">${S.back}</button>
|
|
272
282
|
<div class="pl-title" id="pl-title">Payment Options</div>
|
|
283
|
+
${l.testMode?'<span class="pl-testbadge" title="Test mode \u2014 no real money moves">TEST</span>':""}
|
|
273
284
|
<button class="pl-hic pl-dots" aria-label="More">\u22EF</button>
|
|
274
285
|
<button class="pl-hic pl-close-x" id="pl-close" aria-label="Close">\xD7</button>
|
|
275
286
|
</header>
|
|
276
287
|
<div class="pl-vp"><div class="pl-screen pl-active" id="pl-screen"></div></div>
|
|
277
|
-
<footer class="pl-foot">${
|
|
288
|
+
<footer class="pl-foot">${S.lock}<span>Encrypted & secure payments</span></footer>
|
|
278
289
|
</section>
|
|
279
|
-
</div>`,document.body.appendChild(
|
|
290
|
+
</div>`,document.body.appendChild(o);let v=document.body.style.overflow;document.body.style.overflow="hidden";let E=o.querySelector(".pl-vp"),I=o.querySelector("#pl-title"),j=o.querySelector("#pl-back");function C(){q(),l.onDismiss&&l.onDismiss()}function q(){if(t.stop)try{t.stop()}catch{}clearInterval(t.timer),o.classList.add("pl-closing"),document.body.style.overflow=v,setTimeout(()=>o.remove(),240)}o.querySelector("#pl-close").onclick=C,o.addEventListener("mousedown",e=>{e.target.id==="pl-overlay"&&C()}),j.onclick=()=>{t.screen==="contact"||t.screen==="success"||d("options")};let f={loader:"Payment Options",contact:"Contact Details",options:"Payment Options",success:"Payment Successful"};function d(e){t.screen=e,I.textContent=f[e]||"Payment",j.classList.toggle("pl-show",e==="options"),clearInterval(t.timer);let a=document.createElement("div");a.className="pl-screen pl-active"+(e==="options"?" pl-screen-options":""),a.innerHTML=u[e]?u[e]():"",E.replaceChildren(a),_(e,a)}let n=h.slice(0,4).map(e=>`<img src="${m[e]}" alt="">`).join(""),g=h.map(e=>`<a class="pl-uapp" data-app="${e}" title="${e}"><img src="${m[e]}" alt="${e}"></a>`).join(""),u={loader:()=>`<div class="pl-loader"><div class="pl-bload"><img src="${y(l.logo)}" alt=""><span class="pl-bring"></span><span class="pl-bripple"></span><span class="pl-bripple pl-bripple2"></span></div><p>Securely loading payment options\u2026</p></div>`,contact:()=>`
|
|
280
291
|
<div class="pl-pad">
|
|
281
292
|
<div class="pl-h1">Contact details</div>
|
|
282
293
|
<div class="pl-sub">Enter your details to continue the payment</div>
|
|
283
294
|
<div class="pl-field"><label>Full Name</label>
|
|
284
|
-
<input class="pl-input" id="pl-name" type="text" placeholder="Enter your name" value="${
|
|
295
|
+
<input class="pl-input" id="pl-name" type="text" placeholder="Enter your name" value="${y(l.prefill.name||"")}"></div>
|
|
285
296
|
<div class="pl-field"><label>Mobile Number</label>
|
|
286
297
|
<div class="pl-phone"><div class="pl-cc">+91</div>
|
|
287
|
-
<input class="pl-input" id="pl-mobile" type="tel" maxlength="10" placeholder="Enter mobile number" value="${
|
|
298
|
+
<input class="pl-input" id="pl-mobile" type="tel" maxlength="10" placeholder="Enter mobile number" value="${y(l.prefill.contact||"")}"></div></div>
|
|
288
299
|
<div class="pl-field"><label>Email Address <span class="pl-opt">(optional)</span></label>
|
|
289
|
-
<div class="pl-inwrap"><span class="pl-inicon">${
|
|
290
|
-
<input class="pl-input pl-padleft" id="pl-email" type="email" placeholder="Enter email address" value="${
|
|
291
|
-
<button class="pl-cta" id="pl-continue">Continue <span class="pl-cta-arr">${
|
|
300
|
+
<div class="pl-inwrap"><span class="pl-inicon">${S.mail}</span>
|
|
301
|
+
<input class="pl-input pl-padleft" id="pl-email" type="email" placeholder="Enter email address" value="${y(l.prefill.email||"")}"></div></div>
|
|
302
|
+
<button class="pl-cta" id="pl-continue">Continue <span class="pl-cta-arr">${S.arrow}</span></button>
|
|
292
303
|
<div class="pl-err" id="pl-cerr"></div>
|
|
293
|
-
<div class="pl-trust">${
|
|
304
|
+
<div class="pl-trust">${N}100% secure payments</div>
|
|
294
305
|
</div>`,options:()=>`
|
|
295
306
|
<div class="pl-options">
|
|
296
307
|
<div class="pl-nav" id="pl-nav">
|
|
297
308
|
<div class="pl-navlabel">Recommended</div>
|
|
298
309
|
<div class="pl-navitem active" data-m="upi">
|
|
299
|
-
<div class="pl-ni-ic">${
|
|
310
|
+
<div class="pl-ni-ic">${S.qr}</div>
|
|
300
311
|
<div class="pl-ni-body"><div class="pl-ni-t">UPI</div></div>
|
|
301
|
-
<div class="pl-ni-apps">${
|
|
312
|
+
<div class="pl-ni-apps">${n}</div>
|
|
302
313
|
</div>
|
|
303
314
|
<div class="pl-navlabel">Cards, Banking & More</div>
|
|
304
|
-
${
|
|
315
|
+
${de.map(e=>`
|
|
305
316
|
<div class="pl-navitem pl-disabled" data-m="${e.m}">
|
|
306
|
-
<div class="pl-ni-ic pl-ic-${e.ic}">${
|
|
317
|
+
<div class="pl-ni-ic pl-ic-${e.ic}">${S[e.ic]}</div>
|
|
307
318
|
<div class="pl-ni-body"><div class="pl-ni-t">${e.t}</div><div class="pl-ni-s">${e.s}</div></div>
|
|
308
319
|
<span class="pl-soon">Coming soon</span>
|
|
309
320
|
</div>`).join("")}
|
|
310
321
|
</div>
|
|
311
|
-
<div class="pl-pane" id="pl-pane">${
|
|
322
|
+
<div class="pl-pane" id="pl-pane">${T()}</div>
|
|
312
323
|
</div>`,success:()=>{let e=["#00c060","#3395ff","#ffb020","#ff5a5f","#7a52d8","#19c3c3"];return`
|
|
313
324
|
<div class="pl-success">
|
|
314
325
|
<div class="pl-burst">
|
|
315
|
-
${Array.from({length:18},(
|
|
326
|
+
${Array.from({length:18},(p,s)=>{let c=s/18*Math.PI*2+(s%2?.22:-.22),k=60+s*13%40,P=Math.round(Math.cos(c)*k),U=Math.round(Math.sin(c)*k),J=e[s%e.length],Z=(s%2?1:-1)*(140+s*14),K=s%3===0?6:8,ee=s%4===0?6:12;return`<i class="pl-confetti" style="--tx:${P}px;--ty:${U}px;--c:${J};--r:${Z}deg;width:${K}px;height:${ee}px;border-radius:${s%4===0?"50%":"2px"};animation-delay:${(.15+s*.024).toFixed(3)}s"></i>`}).join("")}
|
|
316
327
|
<span class="pl-ripple"></span><span class="pl-ripple pl-ripple2"></span>
|
|
317
328
|
<div class="pl-check"><svg viewBox="0 0 24 24"><path d="M5 13l4 4L19 7"/></svg></div>
|
|
318
329
|
</div>
|
|
319
330
|
<h2>Payment Successful</h2>
|
|
320
|
-
<p>${r} paid${
|
|
321
|
-
<div class="pl-pid" id="pl-pid">${
|
|
322
|
-
${
|
|
323
|
-
</div>`}};function
|
|
331
|
+
<p>${r} paid${l.name?" to "+y(l.name):""}</p>
|
|
332
|
+
<div class="pl-pid" id="pl-pid">${y(t.order&&t.order.merchant_order_no||"")}</div>
|
|
333
|
+
${t.utr?`<div class="pl-pid pl-utr">UTR ${y(t.utr)}</div>`:""}
|
|
334
|
+
</div>`}};function T(){return`
|
|
324
335
|
<div class="pl-paneh">UPI QR</div>
|
|
325
336
|
<div class="pl-qrcard">
|
|
326
337
|
<div class="pl-qrbox" id="pl-qr"><div class="pl-qrloading"></div></div>
|
|
327
338
|
<div class="pl-qrside">
|
|
328
339
|
<div class="pl-scan">Scan the QR using any UPI App</div>
|
|
329
|
-
<div class="pl-approw" id="pl-applist">${
|
|
340
|
+
<div class="pl-approw" id="pl-applist">${g}</div>
|
|
330
341
|
<div class="pl-validrow"><span class="pl-dot"></span><span id="pl-timer">Valid 5:00</span></div>
|
|
331
342
|
</div>
|
|
332
343
|
</div>
|
|
@@ -335,4 +346,4 @@ var E=Object.defineProperty;var ee=Object.getOwnPropertyDescriptor;var le=Object
|
|
|
335
346
|
<input class="pl-vpa" id="pl-vpa" placeholder="example@okhdfcbank" autocomplete="off" spellcheck="false">
|
|
336
347
|
<button class="pl-verify" id="pl-verify">Verify and Pay</button>
|
|
337
348
|
</div>
|
|
338
|
-
<div id="pl-status" class="pl-pendrow"><span class="pl-mini-spin"></span> Waiting for payment\u2026</div>`}function N(e,t){if(e==="contact")t.querySelector("#pl-continue").onclick=()=>{let n=t.querySelector("#pl-name").value.trim(),s=t.querySelector("#pl-mobile").value.replace(/\D/g,""),v=t.querySelector("#pl-email").value.trim(),q=t.querySelector("#pl-cerr");if(n.length<2)return q.textContent="Please enter your name";if(s.length!==10)return q.textContent="Enter a valid 10-digit mobile number";l.customer={name:n,mobile:s,email:v},i("options")};else if(e==="options"){t.querySelectorAll(".pl-navitem").forEach(s=>s.onclick=()=>{s.dataset.m!=="upi"&&C("Coming soon \u2014 please pay via UPI / QR for now")});let n=t.querySelector("#pl-verify");n&&(n.onclick=()=>{let s=(t.querySelector("#pl-vpa").value||"").trim();if(!/^[\w.\-]{2,}@[a-zA-Z]{2,}$/.test(s))return t.querySelector("#pl-vpa").focus(),C("Enter a valid UPI ID, e.g. name@okhdfcbank");let v=l.order&&(l.order.upi_intent_url||l.order.payment_link);v&&/Android|iPhone|iPad|Mobile/i.test(navigator.userAgent)?location.href=v:C("Scan the QR with your phone\u2019s UPI app to complete the payment.")}),B(),P(!0)}}let $=!1;async function P(e){if(e||i("options"),l.order){L();return}if(!$){$=!0;try{l.customer||(l.customer={name:a.prefill.name||"Customer",mobile:(a.prefill.contact||"").replace(/\D/g,""),email:a.prefill.email||""}),l.order=await a.createOrder(l.customer),L(),V()}catch(t){let n=p.querySelector("#pl-qr");if(n){n.innerHTML=`<div class="pl-qrerr"><span>${f(t.message||"Could not start payment")}</span><button class="pl-retry" id="pl-retry">Try again</button></div>`;let s=n.querySelector("#pl-retry");s&&(s.onclick=()=>{$=!1,n.innerHTML='<div class="pl-qrloading"></div>',P(!0)})}}finally{$=!1}}}function L(){let e=l.order;if(!e)return;Y(e);let t=p.querySelector("#pl-qr");t&&(e.qr_data_url?t.innerHTML=`<img src="${e.qr_data_url}" alt="UPI QR">`:t.innerHTML=`<div class="pl-qrerr"><span>${f(e.qr_unavailable_reason||"QR unavailable \u2014 use a UPI app below")}</span></div>`);let n=e.upi_intent_url||e.payment_link||"#",s=p.querySelector("#pl-applist");s&&s.querySelectorAll(".pl-uapp").forEach(v=>{v.setAttribute("href",n),v.setAttribute("target","_blank"),v.setAttribute("rel","noopener")}),B()}function Y(e){e&&e.expires_at&&e.server_now?l.expiryMs=Date.now()+Math.max(0,e.expires_at-e.server_now):l.expiryMs||(l.expiryMs=Date.now()+5*60*1e3)}function T(){if(l.done)return;let e=p.querySelector("#pl-qr");e&&(e.innerHTML='<div class="pl-qrerr"><span>QR code expired</span><button class="pl-retry" id="pl-regen">Generate new QR</button></div>');let t=p.querySelector("#pl-regen");t&&(t.onclick=H);let n=p.querySelector("#pl-timer");n&&(n.textContent="Expired");let s=p.querySelector(".pl-validrow");s&&s.classList.add("pl-expired")}function H(){if(l.stop){try{l.stop()}catch{}l.stop=null}l.order=null,l.expiryMs=null,$=!1;let e=p.querySelector(".pl-validrow");e&&e.classList.remove("pl-expired");let t=p.querySelector("#pl-qr");t&&(t.innerHTML='<div class="pl-qrloading"></div>'),P(!0)}function V(){!l.order||l.stop||(l.stop=a.watchOrder(l.order.merchant_order_no,e=>{!e||typeof e.status!="number"||(e.status===0?(l.utr=e.utr||l.utr,Q()):e.status===3?(clearInterval(l.timer),l.expiryMs=Date.now(),T(),j("failed","QR expired \u2014 generate a new one")):[1,4].includes(e.status)?j("failed","Payment "+(e.status_label||"failed").toLowerCase()):e.status===6&&j("pending","Processing payment\u2026"))}))}function j(e,t){let n=p.querySelector("#pl-status");n&&(n.className=e==="failed"?"pl-pendrow pl-fail":"pl-pendrow",n.innerHTML=(e==="pending"?'<span class="pl-mini-spin"></span> ':"")+f(t))}function Q(){l.done||(l.done=!0,clearInterval(l.timer),i("success"),a.onSuccess&&a.onSuccess({merchant_order_no:l.order.merchant_order_no,utr:l.utr,amount:a.amount}),l.utr||X())}async function X(){for(let e=0;e<10&&!l.utr;e++){await new Promise(t=>setTimeout(t,4e3));try{let t=await fetch(`${o}/api/order/${l.order.merchant_order_no}`).then(n=>n.json());if(t.utr){l.utr=t.utr;let n=p.querySelector("#pl-pid");n&&l.screen==="success"&&n.insertAdjacentHTML("afterend",`<div class="pl-pid pl-utr">UTR ${f(t.utr)}</div>`);break}}catch{}}}function B(){clearInterval(l.timer);let e=()=>{let t=p.querySelector("#pl-timer");if(!t)return;if(!l.expiryMs){t.textContent="Valid 5:00";return}let n=Math.max(0,Math.round((l.expiryMs-Date.now())/1e3));t.textContent=n>0?"Valid "+pe(n):"Expired",n<=0&&(clearInterval(l.timer),T())};e(),l.timer=setInterval(e,1e3)}function C(e){let t=document.getElementById("pl-toast");t||(t=document.createElement("div"),t.id="pl-toast",document.body.appendChild(t)),t.textContent=e,t.classList.add("pl-show"),clearTimeout(t._t),t._t=setTimeout(()=>t.classList.remove("pl-show"),2600)}return i("loader"),setTimeout(()=>{let e=a.prefill||{};e.name&&(e.contact||"").replace(/\D/g,"").length===10?(l.customer={name:e.name,mobile:e.contact.replace(/\D/g,""),email:e.email||""},i("options")):i("contact");let t=p.querySelector("#pl-splash");t&&(t.classList.add("pl-splash-hide"),setTimeout(()=>t.remove(),450))},1600),{close:d}}var D={apiBase:"https://payulink.io/api",assetBase:"https://assetcdn.payulink.io/checkout/v1/assets",name:null,logo:null,brandColor:null,pollMs:2500},w=class a{constructor(o={}){if(typeof window=="undefined"||typeof document=="undefined")throw new Error("PayuLink checkout runs in the browser only. On React Native use @thepayulink/checkout-react-native; on a server use @thepayulink/server.");let r=o.key||o.keyId||o.key_id;if(!r)throw new Error("PayuLink: `key` (your publishable pl_live_\u2026 / pl_test_\u2026 key) is required");if(/^pl_(live|test)_/.test(r)||console.warn("PayuLink: `key` should look like pl_live_\u2026 or pl_test_\u2026"),o.keySecret||o.key_secret)throw new Error("PayuLink: never pass a key_secret to the browser SDK. It belongs on your server only.");this.options=Object.assign({},D,o,{key:r})}open(o={}){let r=Object.assign({},this.options,o),y=String(r.apiBase||"").replace(/\/$/,""),h=String(r.assetBase||`${y}/assets`).replace(/\/$/,""),l=r.order_id||r.orderId;if(!l)throw new Error("PayuLink: `order_id` is required. Create it on your server with POST /v1/orders.");let p=d=>{typeof r.onError=="function"?r.onError(d):console.error("PayuLink:",d.message||d)},x=null,S=null,I=async d=>{var g,_,c;let u=await fetch(`${y}/v1/checkout/session`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key_id:r.key,order_id:l,customer:d?{name:d.name,contact:d.mobile||d.contact,email:d.email}:void 0})}),i=await u.json().catch(()=>({}));if(!u.ok){let M=new Error(((g=i==null?void 0:i.error)==null?void 0:g.description)||`Could not start payment (HTTP ${u.status})`);throw M.code=(_=i==null?void 0:i.error)==null?void 0:_.code,M.retriable=((c=i==null?void 0:i.error)==null?void 0:c.code)==="GATEWAY_BUSY",M}return x=i,{merchant_order_no:i.order_id,amount:i.amount/100,upi_intent_url:i.upi_intent_url,payment_link:i.payment_link,qr_data_url:i.qr_data_url||null,qr_unavailable_reason:i.qr_unavailable_reason||null,qr_retriable:i.qr_retriable===!0,expires_at:i.expires_at?i.expires_at*1e3:null,server_now:Date.now()}},z=(d,u)=>{let i=async()=>{if(x!=null&&x.session_token)try{let c=await(await fetch(`${y}/v1/checkout/session/${encodeURIComponent(x.session_token)}`)).json().catch(()=>({}));c.status==="paid"&&c.payulink_signature&&(S={payulink_order_id:c.payulink_order_id,payulink_payment_id:c.payulink_payment_id,payulink_signature:c.payulink_signature,utr:c.utr||null,amount:c.amount}),u({status:c.status==="paid"?0:c.status==="failed"?1:c.status==="expired"?3:2,status_label:String(c.status||"pending").toUpperCase(),utr:c.utr||null})}catch{}},g=setInterval(i,r.pollMs||D.pollMs);return i(),()=>clearInterval(g)},k={_inner:null,close(){var d;(d=this._inner)==null||d.close()}};return fetch(`${y}/v1/checkout/order/${encodeURIComponent(l)}?key_id=${encodeURIComponent(r.key)}`).then(async d=>{var i;let u=await d.json().catch(()=>({}));if(!d.ok)throw new Error(((i=u==null?void 0:u.error)==null?void 0:i.description)||"Unknown or expired order");k._inner=A(Object.assign({},{apiBase:y,assetBase:h,name:r.name||u.merchant_name,logo:r.logo||`${h}/payulink-logo.png`,brandColor:r.brandColor,note:r.note,prefill:r.prefill||{},amount:u.amount/100,description:r.description||r.item||u.merchant_name,createOrder:I,watchOrder:z,onSuccess:()=>{let g=S||{payulink_order_id:(x==null?void 0:x.order_id)||l,payulink_payment_id:null,payulink_signature:null};typeof r.handler=="function"&&r.handler(g),typeof r.onSuccess=="function"&&r.onSuccess(g)},onDismiss:()=>{typeof r.onDismiss=="function"&&r.onDismiss()},onError:p}))}).catch(p),k}static open(o={}){return new a(o).open(o)}};typeof window!="undefined"&&(window.PayuLink=w,window.PayulinkCheckout=w);
|
|
349
|
+
<div id="pl-status" class="pl-pendrow"><span class="pl-mini-spin"></span> Waiting for payment\u2026</div>`}function _(e,a){if(e==="contact")a.querySelector("#pl-continue").onclick=()=>{let p=a.querySelector("#pl-name").value.trim(),s=a.querySelector("#pl-mobile").value.replace(/\D/g,""),c=a.querySelector("#pl-email").value.trim(),k=a.querySelector("#pl-cerr");if(p.length<2)return k.textContent="Please enter your name";if(s.length!==10)return k.textContent="Enter a valid 10-digit mobile number";t.customer={name:p,mobile:s,email:c},d("options")};else if(e==="options"){a.querySelectorAll(".pl-navitem").forEach(s=>s.onclick=()=>{s.dataset.m!=="upi"&&L("Coming soon \u2014 please pay via UPI / QR for now")});let p=a.querySelector("#pl-verify");p&&(p.onclick=()=>{let s=(a.querySelector("#pl-vpa").value||"").trim();if(!/^[\w.\-]{2,}@[a-zA-Z]{2,}$/.test(s))return a.querySelector("#pl-vpa").focus(),L("Enter a valid UPI ID, e.g. name@okhdfcbank");let c=t.order&&(t.order.upi_intent_url||t.order.payment_link);c&&/Android|iPhone|iPad|Mobile/i.test(navigator.userAgent)?location.href=c:L("Scan the QR with your phone\u2019s UPI app to complete the payment.")}),A(),w(!0)}}let b=!1;async function w(e){if(e||d("options"),t.order){$();return}if(!b){b=!0;try{t.customer||(t.customer={name:l.prefill.name||"Customer",mobile:(l.prefill.contact||"").replace(/\D/g,""),email:l.prefill.email||""}),t.order=await l.createOrder(t.customer),$(),X()}catch(a){let p=o.querySelector("#pl-qr");if(p){p.innerHTML=`<div class="pl-qrerr"><span>${y(a.message||"Could not start payment")}</span><button class="pl-retry" id="pl-retry">Try again</button></div>`;let s=p.querySelector("#pl-retry");s&&(s.onclick=()=>{b=!1,p.innerHTML='<div class="pl-qrloading"></div>',w(!0)})}}finally{b=!1}}}function $(){let e=t.order;if(!e)return;Q(e);let a=o.querySelector("#pl-qr");a&&(e.qr_data_url?a.innerHTML=`<img src="${e.qr_data_url}" alt="UPI QR">`:a.innerHTML=`<div class="pl-qrerr"><span>${y(e.qr_unavailable_reason||"QR unavailable \u2014 use a UPI app below")}</span></div>`);let p=e.upi_intent_url||e.payment_link||"#",s=o.querySelector("#pl-applist");s&&s.querySelectorAll(".pl-uapp").forEach(c=>{c.setAttribute("href",p),c.setAttribute("target","_blank"),c.setAttribute("rel","noopener")}),e.test_mode&&typeof e.simulate=="function"&&V(e),A()}function V(e){let a=o.querySelector(".pl-scan");a&&(a.textContent="Test mode \u2014 no real payment. Simulate the result below.");let p=o.querySelector("#pl-applist");p&&(p.style.display="none");let s=o.querySelector(".pl-paneh2");s&&(s.textContent="Simulate payment (test mode)");let c=o.querySelector(".pl-vparow");!c||c.dataset.sim||(c.dataset.sim="1",c.innerHTML='<div class="pl-simrow"><button class="pl-sim pl-sim-ok" data-outcome="success">Simulate success</button><button class="pl-sim pl-sim-fail" data-outcome="failure">Simulate failure</button></div>',c.querySelectorAll(".pl-sim").forEach(k=>k.onclick=async()=>{c.querySelectorAll(".pl-sim").forEach(P=>P.disabled=!0),z("pending","Simulating\u2026");try{await e.simulate(k.dataset.outcome)}catch(P){z("failed",P.message||"Simulation failed"),c.querySelectorAll(".pl-sim").forEach(U=>U.disabled=!1)}}))}function Q(e){e&&e.expires_at&&e.server_now?t.expiryMs=Date.now()+Math.max(0,e.expires_at-e.server_now):t.expiryMs||(t.expiryMs=Date.now()+5*60*1e3)}function B(){if(t.done)return;let e=o.querySelector("#pl-qr");e&&(e.innerHTML='<div class="pl-qrerr"><span>QR code expired</span><button class="pl-retry" id="pl-regen">Generate new QR</button></div>');let a=o.querySelector("#pl-regen");a&&(a.onclick=F);let p=o.querySelector("#pl-timer");p&&(p.textContent="Expired");let s=o.querySelector(".pl-validrow");s&&s.classList.add("pl-expired")}function F(){if(t.stop){try{t.stop()}catch{}t.stop=null}t.order=null,t.expiryMs=null,b=!1;let e=o.querySelector(".pl-validrow");e&&e.classList.remove("pl-expired");let a=o.querySelector("#pl-qr");a&&(a.innerHTML='<div class="pl-qrloading"></div>'),w(!0)}function X(){!t.order||t.stop||(t.stop=l.watchOrder(t.order.merchant_order_no,e=>{!e||typeof e.status!="number"||(e.status===0?(t.utr=e.utr||t.utr,G()):e.status===3?(clearInterval(t.timer),t.expiryMs=Date.now(),B(),z("failed","QR expired \u2014 generate a new one")):[1,4].includes(e.status)?z("failed","Payment "+(e.status_label||"failed").toLowerCase()):e.status===6&&z("pending","Processing payment\u2026"))}))}function z(e,a){let p=o.querySelector("#pl-status");p&&(p.className=e==="failed"?"pl-pendrow pl-fail":"pl-pendrow",p.innerHTML=(e==="pending"?'<span class="pl-mini-spin"></span> ':"")+y(a))}function G(){t.done||(t.done=!0,clearInterval(t.timer),d("success"),l.onSuccess&&l.onSuccess({merchant_order_no:t.order.merchant_order_no,utr:t.utr,amount:l.amount}),t.utr||W())}async function W(){if(typeof l.fetchUtr=="function")for(let e=0;e<10&&!t.utr;e++){await new Promise(a=>setTimeout(a,4e3));try{let a=await l.fetchUtr();if(a){t.utr=a;let p=o.querySelector("#pl-pid");p&&t.screen==="success"&&p.insertAdjacentHTML("afterend",`<div class="pl-pid pl-utr">UTR ${y(a)}</div>`);break}}catch{}}}function A(){clearInterval(t.timer);let e=()=>{let a=o.querySelector("#pl-timer");if(!a)return;if(!t.expiryMs){a.textContent="Valid 5:00";return}let p=Math.max(0,Math.round((t.expiryMs-Date.now())/1e3));a.textContent=p>0?"Valid "+se(p):"Expired",p<=0&&(clearInterval(t.timer),B())};e(),t.timer=setInterval(e,1e3)}function L(e){let a=document.getElementById("pl-toast");a||(a=document.createElement("div"),a.id="pl-toast",document.body.appendChild(a)),a.textContent=e,a.classList.add("pl-show"),clearTimeout(a._t),a._t=setTimeout(()=>a.classList.remove("pl-show"),2600)}return d("loader"),setTimeout(()=>{let e=l.prefill||{};e.name&&(e.contact||"").replace(/\D/g,"").length===10?(t.customer={name:e.name,mobile:e.contact.replace(/\D/g,""),email:e.email||""},d("options")):d("contact");let a=o.querySelector("#pl-splash");a&&(a.classList.add("pl-splash-hide"),setTimeout(()=>a.remove(),450))},1600),{close:q}}var Y={apiBase:"https://payulink.io/api",assetBase:"https://assetcdn.payulink.io/checkout/v1/assets",name:null,logo:null,brandColor:null,pollMs:2500},M=class l{constructor(i={}){if(typeof window=="undefined"||typeof document=="undefined")throw new Error("PayuLink checkout runs in the browser only. On React Native use @thepayulink/checkout-react-native; on a server use @thepayulink/server.");let r=i.key||i.keyId||i.key_id;if(!r)throw new Error("PayuLink: `key` (your publishable pl_live_\u2026 / pl_test_\u2026 key) is required");if(/^pl_(live|test)_/.test(r)||console.warn("PayuLink: `key` should look like pl_live_\u2026 or pl_test_\u2026"),i.keySecret||i.key_secret)throw new Error("PayuLink: never pass a key_secret to the browser SDK. It belongs on your server only.");this.options=Object.assign({},Y,i,{key:r})}open(i={}){let r=Object.assign({},this.options,i),m=String(r.apiBase||"").replace(/\/$/,""),h=String(r.assetBase||`${m}/assets`).replace(/\/$/,""),t=r.order_id||r.orderId;if(!t)throw new Error("PayuLink: `order_id` is required. Create it on your server with POST /v1/orders.");let o=f=>{typeof r.onError=="function"?r.onError(f):console.error("PayuLink:",f.message||f)},v=null,E=null,I=async()=>v!=null&&v.session_token?(await fetch(`${m}/v1/checkout/session/${encodeURIComponent(v.session_token)}`)).json().catch(()=>({})):{},j=async f=>{var g,u,T;let d=await fetch(`${m}/v1/checkout/session`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key_id:r.key,order_id:t,customer:f?{name:f.name,contact:f.mobile||f.contact,email:f.email}:void 0})}),n=await d.json().catch(()=>({}));if(!d.ok){let _=new Error(((g=n==null?void 0:n.error)==null?void 0:g.description)||`Could not start payment (HTTP ${d.status})`);throw _.code=(u=n==null?void 0:n.error)==null?void 0:u.code,_.retriable=((T=n==null?void 0:n.error)==null?void 0:T.code)==="GATEWAY_BUSY",_}return v=n,{merchant_order_no:n.order_id,amount:n.amount/100,upi_intent_url:n.upi_intent_url,payment_link:n.payment_link,qr_data_url:n.qr_data_url||null,qr_unavailable_reason:n.qr_unavailable_reason||null,qr_retriable:n.qr_retriable===!0,expires_at:n.expires_at?n.expires_at*1e3:null,server_now:Date.now(),test_mode:n.test_mode===!0,simulate:n.test_mode===!0&&n.simulate_url?async _=>{var $;let b=await fetch(`${m}${n.simulate_url}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({outcome:_})}),w=await b.json().catch(()=>({}));if(!b.ok)throw new Error((($=w==null?void 0:w.error)==null?void 0:$.description)||`Simulation failed (HTTP ${b.status})`);return w}:null}},C=(f,d)=>{let n=async()=>{if(v!=null&&v.session_token)try{let u=await I();u.status==="paid"&&u.payulink_signature&&(E={payulink_order_id:u.payulink_order_id,payulink_payment_id:u.payulink_payment_id,payulink_signature:u.payulink_signature,utr:u.utr||null,amount:u.amount}),d({status:u.status==="paid"?0:u.status==="failed"?1:u.status==="expired"?3:2,status_label:String(u.status||"pending").toUpperCase(),utr:u.utr||null})}catch{}},g=setInterval(n,r.pollMs||Y.pollMs);return n(),()=>clearInterval(g)},q={_inner:null,close(){var f;(f=this._inner)==null||f.close()}};return fetch(`${m}/v1/checkout/order/${encodeURIComponent(t)}?key_id=${encodeURIComponent(r.key)}`).then(async f=>{var n;let d=await f.json().catch(()=>({}));if(!f.ok)throw new Error(((n=d==null?void 0:d.error)==null?void 0:n.description)||"Unknown or expired order");q._inner=H(Object.assign({},{apiBase:m,assetBase:h,name:r.name||d.merchant_name,logo:r.logo||`${h}/payulink-logo.png`,brandColor:r.brandColor||r.theme&&r.theme.color||null,testMode:d.mode==="test",note:r.note,prefill:r.prefill||{},amount:d.amount/100,description:r.description||r.item||d.merchant_name,createOrder:j,watchOrder:C,fetchUtr:async()=>(await I()).utr||null,onSuccess:()=>{let g=E||{payulink_order_id:(v==null?void 0:v.order_id)||t,payulink_payment_id:null,payulink_signature:null};typeof r.handler=="function"&&r.handler(g),typeof r.onSuccess=="function"&&r.onSuccess(g)},onDismiss:()=>{typeof r.onDismiss=="function"&&r.onDismiss()},onError:o}))}).catch(o),q}static open(i={}){return new l(i).open(i)}};typeof window!="undefined"&&(window.PayuLink=M,window.PayulinkCheckout=M);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
1
|
+
var ee=`
|
|
2
2
|
#pl-overlay * { box-sizing: border-box; margin: 0; padding: 0; }
|
|
3
3
|
#pl-overlay { --pl-blue:#3395ff; --pl-blue-dark:#1f86f5; --pl-brand:#0b3068; --pl-ink:#1a2a44; --pl-mut:#6b7c93; --pl-line:#eceff4;
|
|
4
4
|
position: fixed; inset: 0; z-index: 2147483000; font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
|
|
@@ -174,6 +174,15 @@ var K=`
|
|
|
174
174
|
#pl-overlay .pl-vparow { display: flex; flex-direction: column; gap: 12px; border: 1px solid #e6ebf4; border-radius: 14px; padding: 16px; }
|
|
175
175
|
#pl-overlay .pl-vpa { width: 100%; padding: 14px 14px; border: 1px solid #d5dae0; border-radius: 10px; font-size: 15px; font-family: inherit; color: var(--pl-ink); outline: none; transition: border-color .15s, box-shadow .15s; }
|
|
176
176
|
#pl-overlay .pl-vpa:focus { border-color: var(--pl-blue); box-shadow: 0 0 0 3px rgba(51,149,255,.16); }
|
|
177
|
+
#pl-overlay .pl-testbadge { font-size: 10.5px; font-weight: 800; letter-spacing: .6px; color: #92400e; background: #fef3c7; border: 1px solid #f59e0b; padding: 3px 8px; border-radius: 999px; flex-shrink: 0; }
|
|
178
|
+
#pl-overlay .pl-testnote { font-size: 11px; font-weight: 800; letter-spacing: .5px; color: #92400e; background: #fef3c7; border: 1px dashed #f59e0b; padding: 5px 8px; border-radius: 8px; margin-bottom: 10px; text-align: center; }
|
|
179
|
+
#pl-overlay .pl-simrow { display: flex; gap: 10px; }
|
|
180
|
+
#pl-overlay .pl-sim { flex: 1; padding: 13px 10px; border-radius: 10px; font-size: 14px; font-weight: 700; font-family: inherit; cursor: pointer; border: 1px solid transparent; transition: filter .15s, opacity .15s; }
|
|
181
|
+
#pl-overlay .pl-sim:disabled { opacity: .55; cursor: default; }
|
|
182
|
+
#pl-overlay .pl-sim-ok { background: #16a34a; color: #fff; }
|
|
183
|
+
#pl-overlay .pl-sim-fail { background: #fff; color: #b91c1c; border-color: #fca5a5; }
|
|
184
|
+
#pl-overlay .pl-sim:not(:disabled):hover { filter: brightness(.95); }
|
|
185
|
+
@media (max-width: 840px) { #pl-overlay .pl-testnote { display: none; } }
|
|
177
186
|
#pl-overlay .pl-vpa::placeholder { color: #aab2bf; }
|
|
178
187
|
#pl-overlay .pl-verify { width: 100%; padding: 15px; background: #16181f; color: #fff; border: none; border-radius: 10px; font-size: 15px; font-weight: 700; cursor: pointer; font-family: inherit; transition: filter .15s, transform .08s; }
|
|
179
188
|
#pl-overlay .pl-verify:hover { filter: brightness(1.25); }
|
|
@@ -237,7 +246,7 @@ var K=`
|
|
|
237
246
|
#pl-overlay .pl-approw { justify-content: center; }
|
|
238
247
|
#pl-overlay .pl-validrow { margin: 0 auto; }
|
|
239
248
|
}
|
|
240
|
-
`,
|
|
249
|
+
`,A=!1;function D(t=document){if(A&&t.getElementById("pl-checkout-styles"))return;let s=t.createElement("style");s.id="pl-checkout-styles",s.textContent=ee,t.head.appendChild(s),A=!0}var le=t=>{let s=Number(t||0);return"\u20B9"+s.toLocaleString("en-IN",{minimumFractionDigits:s%1===0?0:2,maximumFractionDigits:2})},ae=t=>`${Math.floor(t/60)}:${t%60<10?"0":""}${t%60}`,y=t=>String(t==null?"":t).replace(/[&<>"]/g,s=>({"&":"&","<":"<",">":">",'"':"""})[s]),m=t=>`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${t}</svg>`,_={qr:m('<rect width="5" height="5" x="3" y="3" rx="1"/><rect width="5" height="5" x="16" y="3" rx="1"/><rect width="5" height="5" x="3" y="16" rx="1"/><path d="M21 16h-3a2 2 0 0 0-2 2v3"/><path d="M21 21v.01"/><path d="M12 7v3a2 2 0 0 1-2 2H7"/><path d="M3 12h.01"/><path d="M12 3h.01"/><path d="M12 16v.01"/><path d="M16 12h1"/><path d="M21 12v.01"/><path d="M12 21v-1"/>'),card:m('<rect width="20" height="14" x="2" y="5" rx="2"/><line x1="2" x2="22" y1="10" y2="10"/>'),netbanking:m('<path d="M10 18v-7"/><path d="M11.119 2.205a2 2 0 0 1 1.762 0l7.84 3.846A.5.5 0 0 1 20.5 7h-17a.5.5 0 0 1-.22-.949z"/><path d="M14 18v-7"/><path d="M18 18v-7"/><path d="M3 22h18"/><path d="M6 18v-7"/>'),wallet:m('<path d="M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1"/><path d="M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4"/>'),emi:m('<path d="M16 14v2.2l1.6 1"/><path d="M16 2v4"/><path d="M21 7.5V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h3.5"/><path d="M3 10h5"/><path d="M8 2v4"/><circle cx="16" cy="16" r="6"/>'),back:m('<path d="M15 18l-6-6 6-6"/>'),mail:m('<rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-10 5L2 7"/>'),arrow:m('<path d="M5 12h14M13 6l6 6-6 6"/>'),verified:m('<path d="M9 12l2 2 4-4"/><circle cx="12" cy="12" r="10"/>'),lock:m('<rect width="18" height="11" x="3" y="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>')},R='<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M12 1l9 4v6c0 5.5-3.8 10.7-9 12-5.2-1.3-9-6.5-9-12V5l9-4z"/></svg>',te=[{m:"card",t:"Cards",s:"Visa, Mastercard & RuPay",ic:"card"},{m:"netbanking",t:"Netbanking",s:"All Indian banks",ic:"netbanking"},{m:"wallet",t:"Wallet",s:"Paytm, PhonePe & more",ic:"wallet"},{m:"emi",t:"EMI",s:"EMI via cards & more",ic:"emi"}];function N(t){D(),t=Object.assign({name:"Payment",description:"",amount:0,assetBase:"/assets",logo:"/assets/payulink-logo.png",prefill:{},apiBase:""},t);let s=String(t.apiBase||"").replace(/\/$/,""),r=le(t.amount),h=["gpay","phonepe","paytm","bhim","amazonpay"].reduce((e,l)=>(e[l]=`${t.assetBase}/upi/${l}.png`,e),{}),S=["phonepe","gpay","paytm","bhim","amazonpay"],a={screen:"loader",order:null,customer:null,timer:null,stop:null,done:!1,utr:null,expiryMs:null},i=document.createElement("div");i.id="pl-overlay",t.brandColor&&i.style.setProperty("--pl-brand",t.brandColor),i.innerHTML=`
|
|
241
250
|
<div class="pl-modal" role="dialog" aria-modal="true">
|
|
242
251
|
<div class="pl-splash" id="pl-splash">
|
|
243
252
|
<div class="pl-shield">
|
|
@@ -246,87 +255,89 @@ var K=`
|
|
|
246
255
|
<path d="M60 3 L113 25 V70 C113 105 90 128 60 137 C30 128 7 105 7 70 V25 Z" fill="url(#pl-shgrad)"/>
|
|
247
256
|
</svg>
|
|
248
257
|
<span class="pl-shield-shine"></span>
|
|
249
|
-
<img class="pl-shield-logo" src="${
|
|
258
|
+
<img class="pl-shield-logo" src="${y(t.assetBase)}/payulink-logo.png" alt="payulink">
|
|
250
259
|
</div>
|
|
251
|
-
<div class="pl-splash-foot">Secured By <span class="pl-pb"><img src="${
|
|
260
|
+
<div class="pl-splash-foot">Secured By <span class="pl-pb"><img src="${y(t.assetBase)}/payulink-logo.png" alt=""><strong>payulink</strong></span></div>
|
|
252
261
|
</div>
|
|
253
262
|
<aside class="pl-left">
|
|
254
263
|
<div class="pl-brandrow">
|
|
255
|
-
<div class="pl-logo"><img src="${
|
|
264
|
+
<div class="pl-logo"><img src="${y(t.logo)}" alt=""></div>
|
|
256
265
|
<div class="pl-bmeta">
|
|
257
|
-
<div class="pl-bname">${
|
|
258
|
-
<div class="pl-btrust">${
|
|
266
|
+
<div class="pl-bname">${y(t.name)}</div>
|
|
267
|
+
<div class="pl-btrust">${_.verified}<span>Trusted Business</span></div>
|
|
259
268
|
</div>
|
|
260
269
|
</div>
|
|
261
270
|
<div class="pl-summary">
|
|
271
|
+
${t.testMode?'<div class="pl-testnote">TEST MODE \xB7 no real payment</div>':""}
|
|
262
272
|
<div class="pl-sl">Price Summary</div>
|
|
263
|
-
<div class="pl-samt">${
|
|
264
|
-
<div class="pl-snote">${
|
|
273
|
+
<div class="pl-samt">${r}</div>
|
|
274
|
+
<div class="pl-snote">${y(t.note||"Paying for "+(t.description||t.name))}</div>
|
|
265
275
|
</div>
|
|
266
276
|
<svg class="pl-illus" viewBox="0 0 230 150" fill="none"><rect x="20" y="70" width="58" height="68" rx="5" fill="#fff" fill-opacity=".92"/><path d="M33 70c0-10 5-16 16-16s16 6 16 16" stroke="#fff" stroke-width="3.5"/><rect x="88" y="55" width="68" height="83" rx="5" fill="#fff"/><path d="M104 55c0-12 6-19 18-19s18 7 18 19" stroke="#fff" stroke-width="3.5"/><rect x="150" y="92" width="52" height="46" rx="4" fill="#fff" fill-opacity=".92"/><circle cx="178" cy="40" r="18" fill="#fff"/><path d="M178 30v20M168 40h20" stroke="currentColor" stroke-width="3.5" opacity=".5"/></svg>
|
|
267
|
-
<div class="pl-secured">${
|
|
277
|
+
<div class="pl-secured">${R}Secured by <span class="pl-pb"><img src="${y(t.assetBase)}/payulink-logo.png" alt=""><strong>payulink</strong></span></div>
|
|
268
278
|
</aside>
|
|
269
279
|
<section class="pl-main">
|
|
270
280
|
<header class="pl-head">
|
|
271
|
-
<button class="pl-hic pl-back" id="pl-back" aria-label="Back">${
|
|
281
|
+
<button class="pl-hic pl-back" id="pl-back" aria-label="Back">${_.back}</button>
|
|
272
282
|
<div class="pl-title" id="pl-title">Payment Options</div>
|
|
283
|
+
${t.testMode?'<span class="pl-testbadge" title="Test mode \u2014 no real money moves">TEST</span>':""}
|
|
273
284
|
<button class="pl-hic pl-dots" aria-label="More">\u22EF</button>
|
|
274
285
|
<button class="pl-hic pl-close-x" id="pl-close" aria-label="Close">\xD7</button>
|
|
275
286
|
</header>
|
|
276
287
|
<div class="pl-vp"><div class="pl-screen pl-active" id="pl-screen"></div></div>
|
|
277
|
-
<footer class="pl-foot">${
|
|
288
|
+
<footer class="pl-foot">${_.lock}<span>Encrypted & secure payments</span></footer>
|
|
278
289
|
</section>
|
|
279
|
-
</div>`,document.body.appendChild(
|
|
290
|
+
</div>`,document.body.appendChild(i);let v=document.body.style.overflow;document.body.style.overflow="hidden";let E=i.querySelector(".pl-vp"),I=i.querySelector("#pl-title"),j=i.querySelector("#pl-back");function C(){M(),t.onDismiss&&t.onDismiss()}function M(){if(a.stop)try{a.stop()}catch{}clearInterval(a.timer),i.classList.add("pl-closing"),document.body.style.overflow=v,setTimeout(()=>i.remove(),240)}i.querySelector("#pl-close").onclick=C,i.addEventListener("mousedown",e=>{e.target.id==="pl-overlay"&&C()}),j.onclick=()=>{a.screen==="contact"||a.screen==="success"||d("options")};let f={loader:"Payment Options",contact:"Contact Details",options:"Payment Options",success:"Payment Successful"};function d(e){a.screen=e,I.textContent=f[e]||"Payment",j.classList.toggle("pl-show",e==="options"),clearInterval(a.timer);let l=document.createElement("div");l.className="pl-screen pl-active"+(e==="options"?" pl-screen-options":""),l.innerHTML=u[e]?u[e]():"",E.replaceChildren(l),k(e,l)}let o=S.slice(0,4).map(e=>`<img src="${h[e]}" alt="">`).join(""),x=S.map(e=>`<a class="pl-uapp" data-app="${e}" title="${e}"><img src="${h[e]}" alt="${e}"></a>`).join(""),u={loader:()=>`<div class="pl-loader"><div class="pl-bload"><img src="${y(t.logo)}" alt=""><span class="pl-bring"></span><span class="pl-bripple"></span><span class="pl-bripple pl-bripple2"></span></div><p>Securely loading payment options\u2026</p></div>`,contact:()=>`
|
|
280
291
|
<div class="pl-pad">
|
|
281
292
|
<div class="pl-h1">Contact details</div>
|
|
282
293
|
<div class="pl-sub">Enter your details to continue the payment</div>
|
|
283
294
|
<div class="pl-field"><label>Full Name</label>
|
|
284
|
-
<input class="pl-input" id="pl-name" type="text" placeholder="Enter your name" value="${
|
|
295
|
+
<input class="pl-input" id="pl-name" type="text" placeholder="Enter your name" value="${y(t.prefill.name||"")}"></div>
|
|
285
296
|
<div class="pl-field"><label>Mobile Number</label>
|
|
286
297
|
<div class="pl-phone"><div class="pl-cc">+91</div>
|
|
287
|
-
<input class="pl-input" id="pl-mobile" type="tel" maxlength="10" placeholder="Enter mobile number" value="${
|
|
298
|
+
<input class="pl-input" id="pl-mobile" type="tel" maxlength="10" placeholder="Enter mobile number" value="${y(t.prefill.contact||"")}"></div></div>
|
|
288
299
|
<div class="pl-field"><label>Email Address <span class="pl-opt">(optional)</span></label>
|
|
289
|
-
<div class="pl-inwrap"><span class="pl-inicon">${
|
|
290
|
-
<input class="pl-input pl-padleft" id="pl-email" type="email" placeholder="Enter email address" value="${
|
|
291
|
-
<button class="pl-cta" id="pl-continue">Continue <span class="pl-cta-arr">${
|
|
300
|
+
<div class="pl-inwrap"><span class="pl-inicon">${_.mail}</span>
|
|
301
|
+
<input class="pl-input pl-padleft" id="pl-email" type="email" placeholder="Enter email address" value="${y(t.prefill.email||"")}"></div></div>
|
|
302
|
+
<button class="pl-cta" id="pl-continue">Continue <span class="pl-cta-arr">${_.arrow}</span></button>
|
|
292
303
|
<div class="pl-err" id="pl-cerr"></div>
|
|
293
|
-
<div class="pl-trust">${
|
|
304
|
+
<div class="pl-trust">${R}100% secure payments</div>
|
|
294
305
|
</div>`,options:()=>`
|
|
295
306
|
<div class="pl-options">
|
|
296
307
|
<div class="pl-nav" id="pl-nav">
|
|
297
308
|
<div class="pl-navlabel">Recommended</div>
|
|
298
309
|
<div class="pl-navitem active" data-m="upi">
|
|
299
|
-
<div class="pl-ni-ic">${
|
|
310
|
+
<div class="pl-ni-ic">${_.qr}</div>
|
|
300
311
|
<div class="pl-ni-body"><div class="pl-ni-t">UPI</div></div>
|
|
301
|
-
<div class="pl-ni-apps">${
|
|
312
|
+
<div class="pl-ni-apps">${o}</div>
|
|
302
313
|
</div>
|
|
303
314
|
<div class="pl-navlabel">Cards, Banking & More</div>
|
|
304
|
-
${
|
|
315
|
+
${te.map(e=>`
|
|
305
316
|
<div class="pl-navitem pl-disabled" data-m="${e.m}">
|
|
306
|
-
<div class="pl-ni-ic pl-ic-${e.ic}">${
|
|
317
|
+
<div class="pl-ni-ic pl-ic-${e.ic}">${_[e.ic]}</div>
|
|
307
318
|
<div class="pl-ni-body"><div class="pl-ni-t">${e.t}</div><div class="pl-ni-s">${e.s}</div></div>
|
|
308
319
|
<span class="pl-soon">Coming soon</span>
|
|
309
320
|
</div>`).join("")}
|
|
310
321
|
</div>
|
|
311
|
-
<div class="pl-pane" id="pl-pane">${
|
|
322
|
+
<div class="pl-pane" id="pl-pane">${T()}</div>
|
|
312
323
|
</div>`,success:()=>{let e=["#00c060","#3395ff","#ffb020","#ff5a5f","#7a52d8","#19c3c3"];return`
|
|
313
324
|
<div class="pl-success">
|
|
314
325
|
<div class="pl-burst">
|
|
315
|
-
${Array.from({length:18},(
|
|
326
|
+
${Array.from({length:18},(n,p)=>{let c=p/18*Math.PI*2+(p%2?.22:-.22),w=60+p*13%40,z=Math.round(Math.cos(c)*w),U=Math.round(Math.sin(c)*w),W=e[p%e.length],J=(p%2?1:-1)*(140+p*14),Z=p%3===0?6:8,K=p%4===0?6:12;return`<i class="pl-confetti" style="--tx:${z}px;--ty:${U}px;--c:${W};--r:${J}deg;width:${Z}px;height:${K}px;border-radius:${p%4===0?"50%":"2px"};animation-delay:${(.15+p*.024).toFixed(3)}s"></i>`}).join("")}
|
|
316
327
|
<span class="pl-ripple"></span><span class="pl-ripple pl-ripple2"></span>
|
|
317
328
|
<div class="pl-check"><svg viewBox="0 0 24 24"><path d="M5 13l4 4L19 7"/></svg></div>
|
|
318
329
|
</div>
|
|
319
330
|
<h2>Payment Successful</h2>
|
|
320
|
-
<p>${
|
|
321
|
-
<div class="pl-pid" id="pl-pid">${
|
|
322
|
-
${
|
|
323
|
-
</div>`}};function
|
|
331
|
+
<p>${r} paid${t.name?" to "+y(t.name):""}</p>
|
|
332
|
+
<div class="pl-pid" id="pl-pid">${y(a.order&&a.order.merchant_order_no||"")}</div>
|
|
333
|
+
${a.utr?`<div class="pl-pid pl-utr">UTR ${y(a.utr)}</div>`:""}
|
|
334
|
+
</div>`}};function T(){return`
|
|
324
335
|
<div class="pl-paneh">UPI QR</div>
|
|
325
336
|
<div class="pl-qrcard">
|
|
326
337
|
<div class="pl-qrbox" id="pl-qr"><div class="pl-qrloading"></div></div>
|
|
327
338
|
<div class="pl-qrside">
|
|
328
339
|
<div class="pl-scan">Scan the QR using any UPI App</div>
|
|
329
|
-
<div class="pl-approw" id="pl-applist">${
|
|
340
|
+
<div class="pl-approw" id="pl-applist">${x}</div>
|
|
330
341
|
<div class="pl-validrow"><span class="pl-dot"></span><span id="pl-timer">Valid 5:00</span></div>
|
|
331
342
|
</div>
|
|
332
343
|
</div>
|
|
@@ -335,4 +346,4 @@ var K=`
|
|
|
335
346
|
<input class="pl-vpa" id="pl-vpa" placeholder="example@okhdfcbank" autocomplete="off" spellcheck="false">
|
|
336
347
|
<button class="pl-verify" id="pl-verify">Verify and Pay</button>
|
|
337
348
|
</div>
|
|
338
|
-
<div id="pl-status" class="pl-pendrow"><span class="pl-mini-spin"></span> Waiting for payment\u2026</div>`}function D(e,a){if(e==="contact")a.querySelector("#pl-continue").onclick=()=>{let o=a.querySelector("#pl-name").value.trim(),p=a.querySelector("#pl-mobile").value.replace(/\D/g,""),v=a.querySelector("#pl-email").value.trim(),$=a.querySelector("#pl-cerr");if(o.length<2)return $.textContent="Please enter your name";if(p.length!==10)return $.textContent="Enter a valid 10-digit mobile number";l.customer={name:o,mobile:p,email:v},r("options")};else if(e==="options"){a.querySelectorAll(".pl-navitem").forEach(p=>p.onclick=()=>{p.dataset.m!=="upi"&&C("Coming soon \u2014 please pay via UPI / QR for now")});let o=a.querySelector("#pl-verify");o&&(o.onclick=()=>{let p=(a.querySelector("#pl-vpa").value||"").trim();if(!/^[\w.\-]{2,}@[a-zA-Z]{2,}$/.test(p))return a.querySelector("#pl-vpa").focus(),C("Enter a valid UPI ID, e.g. name@okhdfcbank");let v=l.order&&(l.order.upi_intent_url||l.order.payment_link);v&&/Android|iPhone|iPad|Mobile/i.test(navigator.userAgent)?location.href=v:C("Scan the QR with your phone\u2019s UPI app to complete the payment.")}),T(),P(!0)}}let M=!1;async function P(e){if(e||r("options"),l.order){E();return}if(!M){M=!0;try{l.customer||(l.customer={name:t.prefill.name||"Customer",mobile:(t.prefill.contact||"").replace(/\D/g,""),email:t.prefill.email||""}),l.order=await t.createOrder(l.customer),E(),H()}catch(a){let o=n.querySelector("#pl-qr");if(o){o.innerHTML=`<div class="pl-qrerr"><span>${f(a.message||"Could not start payment")}</span><button class="pl-retry" id="pl-retry">Try again</button></div>`;let p=o.querySelector("#pl-retry");p&&(p.onclick=()=>{M=!1,o.innerHTML='<div class="pl-qrloading"></div>',P(!0)})}}finally{M=!1}}}function E(){let e=l.order;if(!e)return;N(e);let a=n.querySelector("#pl-qr");a&&(e.qr_data_url?a.innerHTML=`<img src="${e.qr_data_url}" alt="UPI QR">`:a.innerHTML=`<div class="pl-qrerr"><span>${f(e.qr_unavailable_reason||"QR unavailable \u2014 use a UPI app below")}</span></div>`);let o=e.upi_intent_url||e.payment_link||"#",p=n.querySelector("#pl-applist");p&&p.querySelectorAll(".pl-uapp").forEach(v=>{v.setAttribute("href",o),v.setAttribute("target","_blank"),v.setAttribute("rel","noopener")}),T()}function N(e){e&&e.expires_at&&e.server_now?l.expiryMs=Date.now()+Math.max(0,e.expires_at-e.server_now):l.expiryMs||(l.expiryMs=Date.now()+5*60*1e3)}function L(){if(l.done)return;let e=n.querySelector("#pl-qr");e&&(e.innerHTML='<div class="pl-qrerr"><span>QR code expired</span><button class="pl-retry" id="pl-regen">Generate new QR</button></div>');let a=n.querySelector("#pl-regen");a&&(a.onclick=Y);let o=n.querySelector("#pl-timer");o&&(o.textContent="Expired");let p=n.querySelector(".pl-validrow");p&&p.classList.add("pl-expired")}function Y(){if(l.stop){try{l.stop()}catch{}l.stop=null}l.order=null,l.expiryMs=null,M=!1;let e=n.querySelector(".pl-validrow");e&&e.classList.remove("pl-expired");let a=n.querySelector("#pl-qr");a&&(a.innerHTML='<div class="pl-qrloading"></div>'),P(!0)}function H(){!l.order||l.stop||(l.stop=t.watchOrder(l.order.merchant_order_no,e=>{!e||typeof e.status!="number"||(e.status===0?(l.utr=e.utr||l.utr,V()):e.status===3?(clearInterval(l.timer),l.expiryMs=Date.now(),L(),j("failed","QR expired \u2014 generate a new one")):[1,4].includes(e.status)?j("failed","Payment "+(e.status_label||"failed").toLowerCase()):e.status===6&&j("pending","Processing payment\u2026"))}))}function j(e,a){let o=n.querySelector("#pl-status");o&&(o.className=e==="failed"?"pl-pendrow pl-fail":"pl-pendrow",o.innerHTML=(e==="pending"?'<span class="pl-mini-spin"></span> ':"")+f(a))}function V(){l.done||(l.done=!0,clearInterval(l.timer),r("success"),t.onSuccess&&t.onSuccess({merchant_order_no:l.order.merchant_order_no,utr:l.utr,amount:t.amount}),l.utr||Q())}async function Q(){for(let e=0;e<10&&!l.utr;e++){await new Promise(a=>setTimeout(a,4e3));try{let a=await fetch(`${s}/api/order/${l.order.merchant_order_no}`).then(o=>o.json());if(a.utr){l.utr=a.utr;let o=n.querySelector("#pl-pid");o&&l.screen==="success"&&o.insertAdjacentHTML("afterend",`<div class="pl-pid pl-utr">UTR ${f(a.utr)}</div>`);break}}catch{}}}function T(){clearInterval(l.timer);let e=()=>{let a=n.querySelector("#pl-timer");if(!a)return;if(!l.expiryMs){a.textContent="Valid 5:00";return}let o=Math.max(0,Math.round((l.expiryMs-Date.now())/1e3));a.textContent=o>0?"Valid "+le(o):"Expired",o<=0&&(clearInterval(l.timer),L())};e(),l.timer=setInterval(e,1e3)}function C(e){let a=document.getElementById("pl-toast");a||(a=document.createElement("div"),a.id="pl-toast",document.body.appendChild(a)),a.textContent=e,a.classList.add("pl-show"),clearTimeout(a._t),a._t=setTimeout(()=>a.classList.remove("pl-show"),2600)}return r("loader"),setTimeout(()=>{let e=t.prefill||{};e.name&&(e.contact||"").replace(/\D/g,"").length===10?(l.customer={name:e.name,mobile:e.contact.replace(/\D/g,""),email:e.email||""},r("options")):r("contact");let a=n.querySelector("#pl-splash");a&&(a.classList.add("pl-splash-hide"),setTimeout(()=>a.remove(),450))},1600),{close:d}}var A={apiBase:"https://payulink.io/api",assetBase:"https://assetcdn.payulink.io/checkout/v1/assets",name:null,logo:null,brandColor:null,pollMs:2500},q=class t{constructor(s={}){if(typeof window=="undefined"||typeof document=="undefined")throw new Error("PayuLink checkout runs in the browser only. On React Native use @thepayulink/checkout-react-native; on a server use @thepayulink/server.");let i=s.key||s.keyId||s.key_id;if(!i)throw new Error("PayuLink: `key` (your publishable pl_live_\u2026 / pl_test_\u2026 key) is required");if(/^pl_(live|test)_/.test(i)||console.warn("PayuLink: `key` should look like pl_live_\u2026 or pl_test_\u2026"),s.keySecret||s.key_secret)throw new Error("PayuLink: never pass a key_secret to the browser SDK. It belongs on your server only.");this.options=Object.assign({},A,s,{key:i})}open(s={}){let i=Object.assign({},this.options,s),m=String(i.apiBase||"").replace(/\/$/,""),b=String(i.assetBase||`${m}/assets`).replace(/\/$/,""),l=i.order_id||i.orderId;if(!l)throw new Error("PayuLink: `order_id` is required. Create it on your server with POST /v1/orders.");let n=d=>{typeof i.onError=="function"?i.onError(d):console.error("PayuLink:",d.message||d)},y=null,S=null,I=async d=>{var x,k,c;let u=await fetch(`${m}/v1/checkout/session`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key_id:i.key,order_id:l,customer:d?{name:d.name,contact:d.mobile||d.contact,email:d.email}:void 0})}),r=await u.json().catch(()=>({}));if(!u.ok){let _=new Error(((x=r==null?void 0:r.error)==null?void 0:x.description)||`Could not start payment (HTTP ${u.status})`);throw _.code=(k=r==null?void 0:r.error)==null?void 0:k.code,_.retriable=((c=r==null?void 0:r.error)==null?void 0:c.code)==="GATEWAY_BUSY",_}return y=r,{merchant_order_no:r.order_id,amount:r.amount/100,upi_intent_url:r.upi_intent_url,payment_link:r.payment_link,qr_data_url:r.qr_data_url||null,qr_unavailable_reason:r.qr_unavailable_reason||null,qr_retriable:r.qr_retriable===!0,expires_at:r.expires_at?r.expires_at*1e3:null,server_now:Date.now()}},z=(d,u)=>{let r=async()=>{if(y!=null&&y.session_token)try{let c=await(await fetch(`${m}/v1/checkout/session/${encodeURIComponent(y.session_token)}`)).json().catch(()=>({}));c.status==="paid"&&c.payulink_signature&&(S={payulink_order_id:c.payulink_order_id,payulink_payment_id:c.payulink_payment_id,payulink_signature:c.payulink_signature,utr:c.utr||null,amount:c.amount}),u({status:c.status==="paid"?0:c.status==="failed"?1:c.status==="expired"?3:2,status_label:String(c.status||"pending").toUpperCase(),utr:c.utr||null})}catch{}},x=setInterval(r,i.pollMs||A.pollMs);return r(),()=>clearInterval(x)},w={_inner:null,close(){var d;(d=this._inner)==null||d.close()}};return fetch(`${m}/v1/checkout/order/${encodeURIComponent(l)}?key_id=${encodeURIComponent(i.key)}`).then(async d=>{var r;let u=await d.json().catch(()=>({}));if(!d.ok)throw new Error(((r=u==null?void 0:u.error)==null?void 0:r.description)||"Unknown or expired order");w._inner=R(Object.assign({},{apiBase:m,assetBase:b,name:i.name||u.merchant_name,logo:i.logo||`${b}/payulink-logo.png`,brandColor:i.brandColor,note:i.note,prefill:i.prefill||{},amount:u.amount/100,description:i.description||i.item||u.merchant_name,createOrder:I,watchOrder:z,onSuccess:()=>{let x=S||{payulink_order_id:(y==null?void 0:y.order_id)||l,payulink_payment_id:null,payulink_signature:null};typeof i.handler=="function"&&i.handler(x),typeof i.onSuccess=="function"&&i.onSuccess(x)},onDismiss:()=>{typeof i.onDismiss=="function"&&i.onDismiss()},onError:n}))}).catch(n),w}static open(s={}){return new t(s).open(s)}};typeof window!="undefined"&&(window.PayuLink=q,window.PayulinkCheckout=q);export{q as default};
|
|
349
|
+
<div id="pl-status" class="pl-pendrow"><span class="pl-mini-spin"></span> Waiting for payment\u2026</div>`}function k(e,l){if(e==="contact")l.querySelector("#pl-continue").onclick=()=>{let n=l.querySelector("#pl-name").value.trim(),p=l.querySelector("#pl-mobile").value.replace(/\D/g,""),c=l.querySelector("#pl-email").value.trim(),w=l.querySelector("#pl-cerr");if(n.length<2)return w.textContent="Please enter your name";if(p.length!==10)return w.textContent="Enter a valid 10-digit mobile number";a.customer={name:n,mobile:p,email:c},d("options")};else if(e==="options"){l.querySelectorAll(".pl-navitem").forEach(p=>p.onclick=()=>{p.dataset.m!=="upi"&&L("Coming soon \u2014 please pay via UPI / QR for now")});let n=l.querySelector("#pl-verify");n&&(n.onclick=()=>{let p=(l.querySelector("#pl-vpa").value||"").trim();if(!/^[\w.\-]{2,}@[a-zA-Z]{2,}$/.test(p))return l.querySelector("#pl-vpa").focus(),L("Enter a valid UPI ID, e.g. name@okhdfcbank");let c=a.order&&(a.order.upi_intent_url||a.order.payment_link);c&&/Android|iPhone|iPad|Mobile/i.test(navigator.userAgent)?location.href=c:L("Scan the QR with your phone\u2019s UPI app to complete the payment.")}),B(),b(!0)}}let g=!1;async function b(e){if(e||d("options"),a.order){q();return}if(!g){g=!0;try{a.customer||(a.customer={name:t.prefill.name||"Customer",mobile:(t.prefill.contact||"").replace(/\D/g,""),email:t.prefill.email||""}),a.order=await t.createOrder(a.customer),q(),F()}catch(l){let n=i.querySelector("#pl-qr");if(n){n.innerHTML=`<div class="pl-qrerr"><span>${y(l.message||"Could not start payment")}</span><button class="pl-retry" id="pl-retry">Try again</button></div>`;let p=n.querySelector("#pl-retry");p&&(p.onclick=()=>{g=!1,n.innerHTML='<div class="pl-qrloading"></div>',b(!0)})}}finally{g=!1}}}function q(){let e=a.order;if(!e)return;V(e);let l=i.querySelector("#pl-qr");l&&(e.qr_data_url?l.innerHTML=`<img src="${e.qr_data_url}" alt="UPI QR">`:l.innerHTML=`<div class="pl-qrerr"><span>${y(e.qr_unavailable_reason||"QR unavailable \u2014 use a UPI app below")}</span></div>`);let n=e.upi_intent_url||e.payment_link||"#",p=i.querySelector("#pl-applist");p&&p.querySelectorAll(".pl-uapp").forEach(c=>{c.setAttribute("href",n),c.setAttribute("target","_blank"),c.setAttribute("rel","noopener")}),e.test_mode&&typeof e.simulate=="function"&&Y(e),B()}function Y(e){let l=i.querySelector(".pl-scan");l&&(l.textContent="Test mode \u2014 no real payment. Simulate the result below.");let n=i.querySelector("#pl-applist");n&&(n.style.display="none");let p=i.querySelector(".pl-paneh2");p&&(p.textContent="Simulate payment (test mode)");let c=i.querySelector(".pl-vparow");!c||c.dataset.sim||(c.dataset.sim="1",c.innerHTML='<div class="pl-simrow"><button class="pl-sim pl-sim-ok" data-outcome="success">Simulate success</button><button class="pl-sim pl-sim-fail" data-outcome="failure">Simulate failure</button></div>',c.querySelectorAll(".pl-sim").forEach(w=>w.onclick=async()=>{c.querySelectorAll(".pl-sim").forEach(z=>z.disabled=!0),$("pending","Simulating\u2026");try{await e.simulate(w.dataset.outcome)}catch(z){$("failed",z.message||"Simulation failed"),c.querySelectorAll(".pl-sim").forEach(U=>U.disabled=!1)}}))}function V(e){e&&e.expires_at&&e.server_now?a.expiryMs=Date.now()+Math.max(0,e.expires_at-e.server_now):a.expiryMs||(a.expiryMs=Date.now()+5*60*1e3)}function O(){if(a.done)return;let e=i.querySelector("#pl-qr");e&&(e.innerHTML='<div class="pl-qrerr"><span>QR code expired</span><button class="pl-retry" id="pl-regen">Generate new QR</button></div>');let l=i.querySelector("#pl-regen");l&&(l.onclick=Q);let n=i.querySelector("#pl-timer");n&&(n.textContent="Expired");let p=i.querySelector(".pl-validrow");p&&p.classList.add("pl-expired")}function Q(){if(a.stop){try{a.stop()}catch{}a.stop=null}a.order=null,a.expiryMs=null,g=!1;let e=i.querySelector(".pl-validrow");e&&e.classList.remove("pl-expired");let l=i.querySelector("#pl-qr");l&&(l.innerHTML='<div class="pl-qrloading"></div>'),b(!0)}function F(){!a.order||a.stop||(a.stop=t.watchOrder(a.order.merchant_order_no,e=>{!e||typeof e.status!="number"||(e.status===0?(a.utr=e.utr||a.utr,X()):e.status===3?(clearInterval(a.timer),a.expiryMs=Date.now(),O(),$("failed","QR expired \u2014 generate a new one")):[1,4].includes(e.status)?$("failed","Payment "+(e.status_label||"failed").toLowerCase()):e.status===6&&$("pending","Processing payment\u2026"))}))}function $(e,l){let n=i.querySelector("#pl-status");n&&(n.className=e==="failed"?"pl-pendrow pl-fail":"pl-pendrow",n.innerHTML=(e==="pending"?'<span class="pl-mini-spin"></span> ':"")+y(l))}function X(){a.done||(a.done=!0,clearInterval(a.timer),d("success"),t.onSuccess&&t.onSuccess({merchant_order_no:a.order.merchant_order_no,utr:a.utr,amount:t.amount}),a.utr||G())}async function G(){if(typeof t.fetchUtr=="function")for(let e=0;e<10&&!a.utr;e++){await new Promise(l=>setTimeout(l,4e3));try{let l=await t.fetchUtr();if(l){a.utr=l;let n=i.querySelector("#pl-pid");n&&a.screen==="success"&&n.insertAdjacentHTML("afterend",`<div class="pl-pid pl-utr">UTR ${y(l)}</div>`);break}}catch{}}}function B(){clearInterval(a.timer);let e=()=>{let l=i.querySelector("#pl-timer");if(!l)return;if(!a.expiryMs){l.textContent="Valid 5:00";return}let n=Math.max(0,Math.round((a.expiryMs-Date.now())/1e3));l.textContent=n>0?"Valid "+ae(n):"Expired",n<=0&&(clearInterval(a.timer),O())};e(),a.timer=setInterval(e,1e3)}function L(e){let l=document.getElementById("pl-toast");l||(l=document.createElement("div"),l.id="pl-toast",document.body.appendChild(l)),l.textContent=e,l.classList.add("pl-show"),clearTimeout(l._t),l._t=setTimeout(()=>l.classList.remove("pl-show"),2600)}return d("loader"),setTimeout(()=>{let e=t.prefill||{};e.name&&(e.contact||"").replace(/\D/g,"").length===10?(a.customer={name:e.name,mobile:e.contact.replace(/\D/g,""),email:e.email||""},d("options")):d("contact");let l=i.querySelector("#pl-splash");l&&(l.classList.add("pl-splash-hide"),setTimeout(()=>l.remove(),450))},1600),{close:M}}var H={apiBase:"https://payulink.io/api",assetBase:"https://assetcdn.payulink.io/checkout/v1/assets",name:null,logo:null,brandColor:null,pollMs:2500},P=class t{constructor(s={}){if(typeof window=="undefined"||typeof document=="undefined")throw new Error("PayuLink checkout runs in the browser only. On React Native use @thepayulink/checkout-react-native; on a server use @thepayulink/server.");let r=s.key||s.keyId||s.key_id;if(!r)throw new Error("PayuLink: `key` (your publishable pl_live_\u2026 / pl_test_\u2026 key) is required");if(/^pl_(live|test)_/.test(r)||console.warn("PayuLink: `key` should look like pl_live_\u2026 or pl_test_\u2026"),s.keySecret||s.key_secret)throw new Error("PayuLink: never pass a key_secret to the browser SDK. It belongs on your server only.");this.options=Object.assign({},H,s,{key:r})}open(s={}){let r=Object.assign({},this.options,s),h=String(r.apiBase||"").replace(/\/$/,""),S=String(r.assetBase||`${h}/assets`).replace(/\/$/,""),a=r.order_id||r.orderId;if(!a)throw new Error("PayuLink: `order_id` is required. Create it on your server with POST /v1/orders.");let i=f=>{typeof r.onError=="function"?r.onError(f):console.error("PayuLink:",f.message||f)},v=null,E=null,I=async()=>v!=null&&v.session_token?(await fetch(`${h}/v1/checkout/session/${encodeURIComponent(v.session_token)}`)).json().catch(()=>({})):{},j=async f=>{var x,u,T;let d=await fetch(`${h}/v1/checkout/session`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key_id:r.key,order_id:a,customer:f?{name:f.name,contact:f.mobile||f.contact,email:f.email}:void 0})}),o=await d.json().catch(()=>({}));if(!d.ok){let k=new Error(((x=o==null?void 0:o.error)==null?void 0:x.description)||`Could not start payment (HTTP ${d.status})`);throw k.code=(u=o==null?void 0:o.error)==null?void 0:u.code,k.retriable=((T=o==null?void 0:o.error)==null?void 0:T.code)==="GATEWAY_BUSY",k}return v=o,{merchant_order_no:o.order_id,amount:o.amount/100,upi_intent_url:o.upi_intent_url,payment_link:o.payment_link,qr_data_url:o.qr_data_url||null,qr_unavailable_reason:o.qr_unavailable_reason||null,qr_retriable:o.qr_retriable===!0,expires_at:o.expires_at?o.expires_at*1e3:null,server_now:Date.now(),test_mode:o.test_mode===!0,simulate:o.test_mode===!0&&o.simulate_url?async k=>{var q;let g=await fetch(`${h}${o.simulate_url}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({outcome:k})}),b=await g.json().catch(()=>({}));if(!g.ok)throw new Error(((q=b==null?void 0:b.error)==null?void 0:q.description)||`Simulation failed (HTTP ${g.status})`);return b}:null}},C=(f,d)=>{let o=async()=>{if(v!=null&&v.session_token)try{let u=await I();u.status==="paid"&&u.payulink_signature&&(E={payulink_order_id:u.payulink_order_id,payulink_payment_id:u.payulink_payment_id,payulink_signature:u.payulink_signature,utr:u.utr||null,amount:u.amount}),d({status:u.status==="paid"?0:u.status==="failed"?1:u.status==="expired"?3:2,status_label:String(u.status||"pending").toUpperCase(),utr:u.utr||null})}catch{}},x=setInterval(o,r.pollMs||H.pollMs);return o(),()=>clearInterval(x)},M={_inner:null,close(){var f;(f=this._inner)==null||f.close()}};return fetch(`${h}/v1/checkout/order/${encodeURIComponent(a)}?key_id=${encodeURIComponent(r.key)}`).then(async f=>{var o;let d=await f.json().catch(()=>({}));if(!f.ok)throw new Error(((o=d==null?void 0:d.error)==null?void 0:o.description)||"Unknown or expired order");M._inner=N(Object.assign({},{apiBase:h,assetBase:S,name:r.name||d.merchant_name,logo:r.logo||`${S}/payulink-logo.png`,brandColor:r.brandColor||r.theme&&r.theme.color||null,testMode:d.mode==="test",note:r.note,prefill:r.prefill||{},amount:d.amount/100,description:r.description||r.item||d.merchant_name,createOrder:j,watchOrder:C,fetchUtr:async()=>(await I()).utr||null,onSuccess:()=>{let x=E||{payulink_order_id:(v==null?void 0:v.order_id)||a,payulink_payment_id:null,payulink_signature:null};typeof r.handler=="function"&&r.handler(x),typeof r.onSuccess=="function"&&r.onSuccess(x)},onDismiss:()=>{typeof r.onDismiss=="function"&&r.onDismiss()},onError:i}))}).catch(i),M}static open(s={}){return new t(s).open(s)}};typeof window!="undefined"&&(window.PayuLink=P,window.PayulinkCheckout=P);export{P as default};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var PayulinkCheckoutNS=(()=>{var
|
|
1
|
+
var PayulinkCheckoutNS=(()=>{var O=Object.defineProperty;var le=Object.getOwnPropertyDescriptor;var ae=Object.getOwnPropertyNames;var te=Object.prototype.hasOwnProperty;var re=(l,i)=>{for(var r in i)O(l,r,{get:i[r],enumerable:!0})},ie=(l,i,r,m)=>{if(i&&typeof i=="object"||typeof i=="function")for(let h of ae(i))!te.call(l,h)&&h!==r&&O(l,h,{get:()=>i[h],enumerable:!(m=le(i,h))||m.enumerable});return l};var oe=l=>ie(O({},"__esModule",{value:!0}),l);var ce={};re(ce,{default:()=>M});var ne=`
|
|
2
2
|
#pl-overlay * { box-sizing: border-box; margin: 0; padding: 0; }
|
|
3
3
|
#pl-overlay { --pl-blue:#3395ff; --pl-blue-dark:#1f86f5; --pl-brand:#0b3068; --pl-ink:#1a2a44; --pl-mut:#6b7c93; --pl-line:#eceff4;
|
|
4
4
|
position: fixed; inset: 0; z-index: 2147483000; font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
|
|
@@ -174,6 +174,15 @@ var PayulinkCheckoutNS=(()=>{var E=Object.defineProperty;var ee=Object.getOwnPro
|
|
|
174
174
|
#pl-overlay .pl-vparow { display: flex; flex-direction: column; gap: 12px; border: 1px solid #e6ebf4; border-radius: 14px; padding: 16px; }
|
|
175
175
|
#pl-overlay .pl-vpa { width: 100%; padding: 14px 14px; border: 1px solid #d5dae0; border-radius: 10px; font-size: 15px; font-family: inherit; color: var(--pl-ink); outline: none; transition: border-color .15s, box-shadow .15s; }
|
|
176
176
|
#pl-overlay .pl-vpa:focus { border-color: var(--pl-blue); box-shadow: 0 0 0 3px rgba(51,149,255,.16); }
|
|
177
|
+
#pl-overlay .pl-testbadge { font-size: 10.5px; font-weight: 800; letter-spacing: .6px; color: #92400e; background: #fef3c7; border: 1px solid #f59e0b; padding: 3px 8px; border-radius: 999px; flex-shrink: 0; }
|
|
178
|
+
#pl-overlay .pl-testnote { font-size: 11px; font-weight: 800; letter-spacing: .5px; color: #92400e; background: #fef3c7; border: 1px dashed #f59e0b; padding: 5px 8px; border-radius: 8px; margin-bottom: 10px; text-align: center; }
|
|
179
|
+
#pl-overlay .pl-simrow { display: flex; gap: 10px; }
|
|
180
|
+
#pl-overlay .pl-sim { flex: 1; padding: 13px 10px; border-radius: 10px; font-size: 14px; font-weight: 700; font-family: inherit; cursor: pointer; border: 1px solid transparent; transition: filter .15s, opacity .15s; }
|
|
181
|
+
#pl-overlay .pl-sim:disabled { opacity: .55; cursor: default; }
|
|
182
|
+
#pl-overlay .pl-sim-ok { background: #16a34a; color: #fff; }
|
|
183
|
+
#pl-overlay .pl-sim-fail { background: #fff; color: #b91c1c; border-color: #fca5a5; }
|
|
184
|
+
#pl-overlay .pl-sim:not(:disabled):hover { filter: brightness(.95); }
|
|
185
|
+
@media (max-width: 840px) { #pl-overlay .pl-testnote { display: none; } }
|
|
177
186
|
#pl-overlay .pl-vpa::placeholder { color: #aab2bf; }
|
|
178
187
|
#pl-overlay .pl-verify { width: 100%; padding: 15px; background: #16181f; color: #fff; border: none; border-radius: 10px; font-size: 15px; font-weight: 700; cursor: pointer; font-family: inherit; transition: filter .15s, transform .08s; }
|
|
179
188
|
#pl-overlay .pl-verify:hover { filter: brightness(1.25); }
|
|
@@ -237,7 +246,7 @@ var PayulinkCheckoutNS=(()=>{var E=Object.defineProperty;var ee=Object.getOwnPro
|
|
|
237
246
|
#pl-overlay .pl-approw { justify-content: center; }
|
|
238
247
|
#pl-overlay .pl-validrow { margin: 0 auto; }
|
|
239
248
|
}
|
|
240
|
-
`,
|
|
249
|
+
`,D=!1;function R(l=document){if(D&&l.getElementById("pl-checkout-styles"))return;let i=l.createElement("style");i.id="pl-checkout-styles",i.textContent=ne,l.head.appendChild(i),D=!0}var pe=l=>{let i=Number(l||0);return"\u20B9"+i.toLocaleString("en-IN",{minimumFractionDigits:i%1===0?0:2,maximumFractionDigits:2})},se=l=>`${Math.floor(l/60)}:${l%60<10?"0":""}${l%60}`,y=l=>String(l==null?"":l).replace(/[&<>"]/g,i=>({"&":"&","<":"<",">":">",'"':"""})[i]),x=l=>`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${l}</svg>`,S={qr:x('<rect width="5" height="5" x="3" y="3" rx="1"/><rect width="5" height="5" x="16" y="3" rx="1"/><rect width="5" height="5" x="3" y="16" rx="1"/><path d="M21 16h-3a2 2 0 0 0-2 2v3"/><path d="M21 21v.01"/><path d="M12 7v3a2 2 0 0 1-2 2H7"/><path d="M3 12h.01"/><path d="M12 3h.01"/><path d="M12 16v.01"/><path d="M16 12h1"/><path d="M21 12v.01"/><path d="M12 21v-1"/>'),card:x('<rect width="20" height="14" x="2" y="5" rx="2"/><line x1="2" x2="22" y1="10" y2="10"/>'),netbanking:x('<path d="M10 18v-7"/><path d="M11.119 2.205a2 2 0 0 1 1.762 0l7.84 3.846A.5.5 0 0 1 20.5 7h-17a.5.5 0 0 1-.22-.949z"/><path d="M14 18v-7"/><path d="M18 18v-7"/><path d="M3 22h18"/><path d="M6 18v-7"/>'),wallet:x('<path d="M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1"/><path d="M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4"/>'),emi:x('<path d="M16 14v2.2l1.6 1"/><path d="M16 2v4"/><path d="M21 7.5V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h3.5"/><path d="M3 10h5"/><path d="M8 2v4"/><circle cx="16" cy="16" r="6"/>'),back:x('<path d="M15 18l-6-6 6-6"/>'),mail:x('<rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-10 5L2 7"/>'),arrow:x('<path d="M5 12h14M13 6l6 6-6 6"/>'),verified:x('<path d="M9 12l2 2 4-4"/><circle cx="12" cy="12" r="10"/>'),lock:x('<rect width="18" height="11" x="3" y="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>')},N='<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M12 1l9 4v6c0 5.5-3.8 10.7-9 12-5.2-1.3-9-6.5-9-12V5l9-4z"/></svg>',de=[{m:"card",t:"Cards",s:"Visa, Mastercard & RuPay",ic:"card"},{m:"netbanking",t:"Netbanking",s:"All Indian banks",ic:"netbanking"},{m:"wallet",t:"Wallet",s:"Paytm, PhonePe & more",ic:"wallet"},{m:"emi",t:"EMI",s:"EMI via cards & more",ic:"emi"}];function H(l){R(),l=Object.assign({name:"Payment",description:"",amount:0,assetBase:"/assets",logo:"/assets/payulink-logo.png",prefill:{},apiBase:""},l);let i=String(l.apiBase||"").replace(/\/$/,""),r=pe(l.amount),m=["gpay","phonepe","paytm","bhim","amazonpay"].reduce((e,a)=>(e[a]=`${l.assetBase}/upi/${a}.png`,e),{}),h=["phonepe","gpay","paytm","bhim","amazonpay"],t={screen:"loader",order:null,customer:null,timer:null,stop:null,done:!1,utr:null,expiryMs:null},o=document.createElement("div");o.id="pl-overlay",l.brandColor&&o.style.setProperty("--pl-brand",l.brandColor),o.innerHTML=`
|
|
241
250
|
<div class="pl-modal" role="dialog" aria-modal="true">
|
|
242
251
|
<div class="pl-splash" id="pl-splash">
|
|
243
252
|
<div class="pl-shield">
|
|
@@ -246,87 +255,89 @@ var PayulinkCheckoutNS=(()=>{var E=Object.defineProperty;var ee=Object.getOwnPro
|
|
|
246
255
|
<path d="M60 3 L113 25 V70 C113 105 90 128 60 137 C30 128 7 105 7 70 V25 Z" fill="url(#pl-shgrad)"/>
|
|
247
256
|
</svg>
|
|
248
257
|
<span class="pl-shield-shine"></span>
|
|
249
|
-
<img class="pl-shield-logo" src="${
|
|
258
|
+
<img class="pl-shield-logo" src="${y(l.assetBase)}/payulink-logo.png" alt="payulink">
|
|
250
259
|
</div>
|
|
251
|
-
<div class="pl-splash-foot">Secured By <span class="pl-pb"><img src="${
|
|
260
|
+
<div class="pl-splash-foot">Secured By <span class="pl-pb"><img src="${y(l.assetBase)}/payulink-logo.png" alt=""><strong>payulink</strong></span></div>
|
|
252
261
|
</div>
|
|
253
262
|
<aside class="pl-left">
|
|
254
263
|
<div class="pl-brandrow">
|
|
255
|
-
<div class="pl-logo"><img src="${
|
|
264
|
+
<div class="pl-logo"><img src="${y(l.logo)}" alt=""></div>
|
|
256
265
|
<div class="pl-bmeta">
|
|
257
|
-
<div class="pl-bname">${
|
|
258
|
-
<div class="pl-btrust">${
|
|
266
|
+
<div class="pl-bname">${y(l.name)}</div>
|
|
267
|
+
<div class="pl-btrust">${S.verified}<span>Trusted Business</span></div>
|
|
259
268
|
</div>
|
|
260
269
|
</div>
|
|
261
270
|
<div class="pl-summary">
|
|
271
|
+
${l.testMode?'<div class="pl-testnote">TEST MODE \xB7 no real payment</div>':""}
|
|
262
272
|
<div class="pl-sl">Price Summary</div>
|
|
263
273
|
<div class="pl-samt">${r}</div>
|
|
264
|
-
<div class="pl-snote">${
|
|
274
|
+
<div class="pl-snote">${y(l.note||"Paying for "+(l.description||l.name))}</div>
|
|
265
275
|
</div>
|
|
266
276
|
<svg class="pl-illus" viewBox="0 0 230 150" fill="none"><rect x="20" y="70" width="58" height="68" rx="5" fill="#fff" fill-opacity=".92"/><path d="M33 70c0-10 5-16 16-16s16 6 16 16" stroke="#fff" stroke-width="3.5"/><rect x="88" y="55" width="68" height="83" rx="5" fill="#fff"/><path d="M104 55c0-12 6-19 18-19s18 7 18 19" stroke="#fff" stroke-width="3.5"/><rect x="150" y="92" width="52" height="46" rx="4" fill="#fff" fill-opacity=".92"/><circle cx="178" cy="40" r="18" fill="#fff"/><path d="M178 30v20M168 40h20" stroke="currentColor" stroke-width="3.5" opacity=".5"/></svg>
|
|
267
|
-
<div class="pl-secured">${
|
|
277
|
+
<div class="pl-secured">${N}Secured by <span class="pl-pb"><img src="${y(l.assetBase)}/payulink-logo.png" alt=""><strong>payulink</strong></span></div>
|
|
268
278
|
</aside>
|
|
269
279
|
<section class="pl-main">
|
|
270
280
|
<header class="pl-head">
|
|
271
|
-
<button class="pl-hic pl-back" id="pl-back" aria-label="Back">${
|
|
281
|
+
<button class="pl-hic pl-back" id="pl-back" aria-label="Back">${S.back}</button>
|
|
272
282
|
<div class="pl-title" id="pl-title">Payment Options</div>
|
|
283
|
+
${l.testMode?'<span class="pl-testbadge" title="Test mode \u2014 no real money moves">TEST</span>':""}
|
|
273
284
|
<button class="pl-hic pl-dots" aria-label="More">\u22EF</button>
|
|
274
285
|
<button class="pl-hic pl-close-x" id="pl-close" aria-label="Close">\xD7</button>
|
|
275
286
|
</header>
|
|
276
287
|
<div class="pl-vp"><div class="pl-screen pl-active" id="pl-screen"></div></div>
|
|
277
|
-
<footer class="pl-foot">${
|
|
288
|
+
<footer class="pl-foot">${S.lock}<span>Encrypted & secure payments</span></footer>
|
|
278
289
|
</section>
|
|
279
|
-
</div>`,document.body.appendChild(
|
|
290
|
+
</div>`,document.body.appendChild(o);let v=document.body.style.overflow;document.body.style.overflow="hidden";let E=o.querySelector(".pl-vp"),I=o.querySelector("#pl-title"),j=o.querySelector("#pl-back");function C(){q(),l.onDismiss&&l.onDismiss()}function q(){if(t.stop)try{t.stop()}catch{}clearInterval(t.timer),o.classList.add("pl-closing"),document.body.style.overflow=v,setTimeout(()=>o.remove(),240)}o.querySelector("#pl-close").onclick=C,o.addEventListener("mousedown",e=>{e.target.id==="pl-overlay"&&C()}),j.onclick=()=>{t.screen==="contact"||t.screen==="success"||d("options")};let f={loader:"Payment Options",contact:"Contact Details",options:"Payment Options",success:"Payment Successful"};function d(e){t.screen=e,I.textContent=f[e]||"Payment",j.classList.toggle("pl-show",e==="options"),clearInterval(t.timer);let a=document.createElement("div");a.className="pl-screen pl-active"+(e==="options"?" pl-screen-options":""),a.innerHTML=u[e]?u[e]():"",E.replaceChildren(a),_(e,a)}let n=h.slice(0,4).map(e=>`<img src="${m[e]}" alt="">`).join(""),g=h.map(e=>`<a class="pl-uapp" data-app="${e}" title="${e}"><img src="${m[e]}" alt="${e}"></a>`).join(""),u={loader:()=>`<div class="pl-loader"><div class="pl-bload"><img src="${y(l.logo)}" alt=""><span class="pl-bring"></span><span class="pl-bripple"></span><span class="pl-bripple pl-bripple2"></span></div><p>Securely loading payment options\u2026</p></div>`,contact:()=>`
|
|
280
291
|
<div class="pl-pad">
|
|
281
292
|
<div class="pl-h1">Contact details</div>
|
|
282
293
|
<div class="pl-sub">Enter your details to continue the payment</div>
|
|
283
294
|
<div class="pl-field"><label>Full Name</label>
|
|
284
|
-
<input class="pl-input" id="pl-name" type="text" placeholder="Enter your name" value="${
|
|
295
|
+
<input class="pl-input" id="pl-name" type="text" placeholder="Enter your name" value="${y(l.prefill.name||"")}"></div>
|
|
285
296
|
<div class="pl-field"><label>Mobile Number</label>
|
|
286
297
|
<div class="pl-phone"><div class="pl-cc">+91</div>
|
|
287
|
-
<input class="pl-input" id="pl-mobile" type="tel" maxlength="10" placeholder="Enter mobile number" value="${
|
|
298
|
+
<input class="pl-input" id="pl-mobile" type="tel" maxlength="10" placeholder="Enter mobile number" value="${y(l.prefill.contact||"")}"></div></div>
|
|
288
299
|
<div class="pl-field"><label>Email Address <span class="pl-opt">(optional)</span></label>
|
|
289
|
-
<div class="pl-inwrap"><span class="pl-inicon">${
|
|
290
|
-
<input class="pl-input pl-padleft" id="pl-email" type="email" placeholder="Enter email address" value="${
|
|
291
|
-
<button class="pl-cta" id="pl-continue">Continue <span class="pl-cta-arr">${
|
|
300
|
+
<div class="pl-inwrap"><span class="pl-inicon">${S.mail}</span>
|
|
301
|
+
<input class="pl-input pl-padleft" id="pl-email" type="email" placeholder="Enter email address" value="${y(l.prefill.email||"")}"></div></div>
|
|
302
|
+
<button class="pl-cta" id="pl-continue">Continue <span class="pl-cta-arr">${S.arrow}</span></button>
|
|
292
303
|
<div class="pl-err" id="pl-cerr"></div>
|
|
293
|
-
<div class="pl-trust">${
|
|
304
|
+
<div class="pl-trust">${N}100% secure payments</div>
|
|
294
305
|
</div>`,options:()=>`
|
|
295
306
|
<div class="pl-options">
|
|
296
307
|
<div class="pl-nav" id="pl-nav">
|
|
297
308
|
<div class="pl-navlabel">Recommended</div>
|
|
298
309
|
<div class="pl-navitem active" data-m="upi">
|
|
299
|
-
<div class="pl-ni-ic">${
|
|
310
|
+
<div class="pl-ni-ic">${S.qr}</div>
|
|
300
311
|
<div class="pl-ni-body"><div class="pl-ni-t">UPI</div></div>
|
|
301
|
-
<div class="pl-ni-apps">${
|
|
312
|
+
<div class="pl-ni-apps">${n}</div>
|
|
302
313
|
</div>
|
|
303
314
|
<div class="pl-navlabel">Cards, Banking & More</div>
|
|
304
|
-
${
|
|
315
|
+
${de.map(e=>`
|
|
305
316
|
<div class="pl-navitem pl-disabled" data-m="${e.m}">
|
|
306
|
-
<div class="pl-ni-ic pl-ic-${e.ic}">${
|
|
317
|
+
<div class="pl-ni-ic pl-ic-${e.ic}">${S[e.ic]}</div>
|
|
307
318
|
<div class="pl-ni-body"><div class="pl-ni-t">${e.t}</div><div class="pl-ni-s">${e.s}</div></div>
|
|
308
319
|
<span class="pl-soon">Coming soon</span>
|
|
309
320
|
</div>`).join("")}
|
|
310
321
|
</div>
|
|
311
|
-
<div class="pl-pane" id="pl-pane">${
|
|
322
|
+
<div class="pl-pane" id="pl-pane">${T()}</div>
|
|
312
323
|
</div>`,success:()=>{let e=["#00c060","#3395ff","#ffb020","#ff5a5f","#7a52d8","#19c3c3"];return`
|
|
313
324
|
<div class="pl-success">
|
|
314
325
|
<div class="pl-burst">
|
|
315
|
-
${Array.from({length:18},(
|
|
326
|
+
${Array.from({length:18},(p,s)=>{let c=s/18*Math.PI*2+(s%2?.22:-.22),k=60+s*13%40,P=Math.round(Math.cos(c)*k),U=Math.round(Math.sin(c)*k),J=e[s%e.length],Z=(s%2?1:-1)*(140+s*14),K=s%3===0?6:8,ee=s%4===0?6:12;return`<i class="pl-confetti" style="--tx:${P}px;--ty:${U}px;--c:${J};--r:${Z}deg;width:${K}px;height:${ee}px;border-radius:${s%4===0?"50%":"2px"};animation-delay:${(.15+s*.024).toFixed(3)}s"></i>`}).join("")}
|
|
316
327
|
<span class="pl-ripple"></span><span class="pl-ripple pl-ripple2"></span>
|
|
317
328
|
<div class="pl-check"><svg viewBox="0 0 24 24"><path d="M5 13l4 4L19 7"/></svg></div>
|
|
318
329
|
</div>
|
|
319
330
|
<h2>Payment Successful</h2>
|
|
320
|
-
<p>${r} paid${
|
|
321
|
-
<div class="pl-pid" id="pl-pid">${
|
|
322
|
-
${
|
|
323
|
-
</div>`}};function
|
|
331
|
+
<p>${r} paid${l.name?" to "+y(l.name):""}</p>
|
|
332
|
+
<div class="pl-pid" id="pl-pid">${y(t.order&&t.order.merchant_order_no||"")}</div>
|
|
333
|
+
${t.utr?`<div class="pl-pid pl-utr">UTR ${y(t.utr)}</div>`:""}
|
|
334
|
+
</div>`}};function T(){return`
|
|
324
335
|
<div class="pl-paneh">UPI QR</div>
|
|
325
336
|
<div class="pl-qrcard">
|
|
326
337
|
<div class="pl-qrbox" id="pl-qr"><div class="pl-qrloading"></div></div>
|
|
327
338
|
<div class="pl-qrside">
|
|
328
339
|
<div class="pl-scan">Scan the QR using any UPI App</div>
|
|
329
|
-
<div class="pl-approw" id="pl-applist">${
|
|
340
|
+
<div class="pl-approw" id="pl-applist">${g}</div>
|
|
330
341
|
<div class="pl-validrow"><span class="pl-dot"></span><span id="pl-timer">Valid 5:00</span></div>
|
|
331
342
|
</div>
|
|
332
343
|
</div>
|
|
@@ -335,5 +346,5 @@ var PayulinkCheckoutNS=(()=>{var E=Object.defineProperty;var ee=Object.getOwnPro
|
|
|
335
346
|
<input class="pl-vpa" id="pl-vpa" placeholder="example@okhdfcbank" autocomplete="off" spellcheck="false">
|
|
336
347
|
<button class="pl-verify" id="pl-verify">Verify and Pay</button>
|
|
337
348
|
</div>
|
|
338
|
-
<div id="pl-status" class="pl-pendrow"><span class="pl-mini-spin"></span> Waiting for payment\u2026</div>`}function N(e,t){if(e==="contact")t.querySelector("#pl-continue").onclick=()=>{let n=t.querySelector("#pl-name").value.trim(),s=t.querySelector("#pl-mobile").value.replace(/\D/g,""),v=t.querySelector("#pl-email").value.trim(),q=t.querySelector("#pl-cerr");if(n.length<2)return q.textContent="Please enter your name";if(s.length!==10)return q.textContent="Enter a valid 10-digit mobile number";l.customer={name:n,mobile:s,email:v},i("options")};else if(e==="options"){t.querySelectorAll(".pl-navitem").forEach(s=>s.onclick=()=>{s.dataset.m!=="upi"&&C("Coming soon \u2014 please pay via UPI / QR for now")});let n=t.querySelector("#pl-verify");n&&(n.onclick=()=>{let s=(t.querySelector("#pl-vpa").value||"").trim();if(!/^[\w.\-]{2,}@[a-zA-Z]{2,}$/.test(s))return t.querySelector("#pl-vpa").focus(),C("Enter a valid UPI ID, e.g. name@okhdfcbank");let v=l.order&&(l.order.upi_intent_url||l.order.payment_link);v&&/Android|iPhone|iPad|Mobile/i.test(navigator.userAgent)?location.href=v:C("Scan the QR with your phone\u2019s UPI app to complete the payment.")}),B(),P(!0)}}let $=!1;async function P(e){if(e||i("options"),l.order){L();return}if(!$){$=!0;try{l.customer||(l.customer={name:a.prefill.name||"Customer",mobile:(a.prefill.contact||"").replace(/\D/g,""),email:a.prefill.email||""}),l.order=await a.createOrder(l.customer),L(),V()}catch(t){let n=p.querySelector("#pl-qr");if(n){n.innerHTML=`<div class="pl-qrerr"><span>${f(t.message||"Could not start payment")}</span><button class="pl-retry" id="pl-retry">Try again</button></div>`;let s=n.querySelector("#pl-retry");s&&(s.onclick=()=>{$=!1,n.innerHTML='<div class="pl-qrloading"></div>',P(!0)})}}finally{$=!1}}}function L(){let e=l.order;if(!e)return;Y(e);let t=p.querySelector("#pl-qr");t&&(e.qr_data_url?t.innerHTML=`<img src="${e.qr_data_url}" alt="UPI QR">`:t.innerHTML=`<div class="pl-qrerr"><span>${f(e.qr_unavailable_reason||"QR unavailable \u2014 use a UPI app below")}</span></div>`);let n=e.upi_intent_url||e.payment_link||"#",s=p.querySelector("#pl-applist");s&&s.querySelectorAll(".pl-uapp").forEach(v=>{v.setAttribute("href",n),v.setAttribute("target","_blank"),v.setAttribute("rel","noopener")}),B()}function Y(e){e&&e.expires_at&&e.server_now?l.expiryMs=Date.now()+Math.max(0,e.expires_at-e.server_now):l.expiryMs||(l.expiryMs=Date.now()+5*60*1e3)}function T(){if(l.done)return;let e=p.querySelector("#pl-qr");e&&(e.innerHTML='<div class="pl-qrerr"><span>QR code expired</span><button class="pl-retry" id="pl-regen">Generate new QR</button></div>');let t=p.querySelector("#pl-regen");t&&(t.onclick=H);let n=p.querySelector("#pl-timer");n&&(n.textContent="Expired");let s=p.querySelector(".pl-validrow");s&&s.classList.add("pl-expired")}function H(){if(l.stop){try{l.stop()}catch{}l.stop=null}l.order=null,l.expiryMs=null,$=!1;let e=p.querySelector(".pl-validrow");e&&e.classList.remove("pl-expired");let t=p.querySelector("#pl-qr");t&&(t.innerHTML='<div class="pl-qrloading"></div>'),P(!0)}function V(){!l.order||l.stop||(l.stop=a.watchOrder(l.order.merchant_order_no,e=>{!e||typeof e.status!="number"||(e.status===0?(l.utr=e.utr||l.utr,Q()):e.status===3?(clearInterval(l.timer),l.expiryMs=Date.now(),T(),j("failed","QR expired \u2014 generate a new one")):[1,4].includes(e.status)?j("failed","Payment "+(e.status_label||"failed").toLowerCase()):e.status===6&&j("pending","Processing payment\u2026"))}))}function j(e,t){let n=p.querySelector("#pl-status");n&&(n.className=e==="failed"?"pl-pendrow pl-fail":"pl-pendrow",n.innerHTML=(e==="pending"?'<span class="pl-mini-spin"></span> ':"")+f(t))}function Q(){l.done||(l.done=!0,clearInterval(l.timer),i("success"),a.onSuccess&&a.onSuccess({merchant_order_no:l.order.merchant_order_no,utr:l.utr,amount:a.amount}),l.utr||X())}async function X(){for(let e=0;e<10&&!l.utr;e++){await new Promise(t=>setTimeout(t,4e3));try{let t=await fetch(`${o}/api/order/${l.order.merchant_order_no}`).then(n=>n.json());if(t.utr){l.utr=t.utr;let n=p.querySelector("#pl-pid");n&&l.screen==="success"&&n.insertAdjacentHTML("afterend",`<div class="pl-pid pl-utr">UTR ${f(t.utr)}</div>`);break}}catch{}}}function B(){clearInterval(l.timer);let e=()=>{let t=p.querySelector("#pl-timer");if(!t)return;if(!l.expiryMs){t.textContent="Valid 5:00";return}let n=Math.max(0,Math.round((l.expiryMs-Date.now())/1e3));t.textContent=n>0?"Valid "+pe(n):"Expired",n<=0&&(clearInterval(l.timer),T())};e(),l.timer=setInterval(e,1e3)}function C(e){let t=document.getElementById("pl-toast");t||(t=document.createElement("div"),t.id="pl-toast",document.body.appendChild(t)),t.textContent=e,t.classList.add("pl-show"),clearTimeout(t._t),t._t=setTimeout(()=>t.classList.remove("pl-show"),2600)}return i("loader"),setTimeout(()=>{let e=a.prefill||{};e.name&&(e.contact||"").replace(/\D/g,"").length===10?(l.customer={name:e.name,mobile:e.contact.replace(/\D/g,""),email:e.email||""},i("options")):i("contact");let t=p.querySelector("#pl-splash");t&&(t.classList.add("pl-splash-hide"),setTimeout(()=>t.remove(),450))},1600),{close:d}}var D={apiBase:"https://payulink.io/api",assetBase:"https://assetcdn.payulink.io/checkout/v1/assets",name:null,logo:null,brandColor:null,pollMs:2500},w=class a{constructor(o={}){if(typeof window=="undefined"||typeof document=="undefined")throw new Error("PayuLink checkout runs in the browser only. On React Native use @thepayulink/checkout-react-native; on a server use @thepayulink/server.");let r=o.key||o.keyId||o.key_id;if(!r)throw new Error("PayuLink: `key` (your publishable pl_live_\u2026 / pl_test_\u2026 key) is required");if(/^pl_(live|test)_/.test(r)||console.warn("PayuLink: `key` should look like pl_live_\u2026 or pl_test_\u2026"),o.keySecret||o.key_secret)throw new Error("PayuLink: never pass a key_secret to the browser SDK. It belongs on your server only.");this.options=Object.assign({},D,o,{key:r})}open(o={}){let r=Object.assign({},this.options,o),y=String(r.apiBase||"").replace(/\/$/,""),h=String(r.assetBase||`${y}/assets`).replace(/\/$/,""),l=r.order_id||r.orderId;if(!l)throw new Error("PayuLink: `order_id` is required. Create it on your server with POST /v1/orders.");let p=d=>{typeof r.onError=="function"?r.onError(d):console.error("PayuLink:",d.message||d)},x=null,S=null,I=async d=>{var g,_,c;let u=await fetch(`${y}/v1/checkout/session`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key_id:r.key,order_id:l,customer:d?{name:d.name,contact:d.mobile||d.contact,email:d.email}:void 0})}),i=await u.json().catch(()=>({}));if(!u.ok){let M=new Error(((g=i==null?void 0:i.error)==null?void 0:g.description)||`Could not start payment (HTTP ${u.status})`);throw M.code=(_=i==null?void 0:i.error)==null?void 0:_.code,M.retriable=((c=i==null?void 0:i.error)==null?void 0:c.code)==="GATEWAY_BUSY",M}return x=i,{merchant_order_no:i.order_id,amount:i.amount/100,upi_intent_url:i.upi_intent_url,payment_link:i.payment_link,qr_data_url:i.qr_data_url||null,qr_unavailable_reason:i.qr_unavailable_reason||null,qr_retriable:i.qr_retriable===!0,expires_at:i.expires_at?i.expires_at*1e3:null,server_now:Date.now()}},z=(d,u)=>{let i=async()=>{if(x!=null&&x.session_token)try{let c=await(await fetch(`${y}/v1/checkout/session/${encodeURIComponent(x.session_token)}`)).json().catch(()=>({}));c.status==="paid"&&c.payulink_signature&&(S={payulink_order_id:c.payulink_order_id,payulink_payment_id:c.payulink_payment_id,payulink_signature:c.payulink_signature,utr:c.utr||null,amount:c.amount}),u({status:c.status==="paid"?0:c.status==="failed"?1:c.status==="expired"?3:2,status_label:String(c.status||"pending").toUpperCase(),utr:c.utr||null})}catch{}},g=setInterval(i,r.pollMs||D.pollMs);return i(),()=>clearInterval(g)},k={_inner:null,close(){var d;(d=this._inner)==null||d.close()}};return fetch(`${y}/v1/checkout/order/${encodeURIComponent(l)}?key_id=${encodeURIComponent(r.key)}`).then(async d=>{var i;let u=await d.json().catch(()=>({}));if(!d.ok)throw new Error(((i=u==null?void 0:u.error)==null?void 0:i.description)||"Unknown or expired order");k._inner=A(Object.assign({},{apiBase:y,assetBase:h,name:r.name||u.merchant_name,logo:r.logo||`${h}/payulink-logo.png`,brandColor:r.brandColor,note:r.note,prefill:r.prefill||{},amount:u.amount/100,description:r.description||r.item||u.merchant_name,createOrder:I,watchOrder:z,onSuccess:()=>{let g=S||{payulink_order_id:(x==null?void 0:x.order_id)||l,payulink_payment_id:null,payulink_signature:null};typeof r.handler=="function"&&r.handler(g),typeof r.onSuccess=="function"&&r.onSuccess(g)},onDismiss:()=>{typeof r.onDismiss=="function"&&r.onDismiss()},onError:p}))}).catch(p),k}static open(o={}){return new a(o).open(o)}};typeof window!="undefined"&&(window.PayuLink=w,window.PayulinkCheckout=w);return ie(de);})();
|
|
349
|
+
<div id="pl-status" class="pl-pendrow"><span class="pl-mini-spin"></span> Waiting for payment\u2026</div>`}function _(e,a){if(e==="contact")a.querySelector("#pl-continue").onclick=()=>{let p=a.querySelector("#pl-name").value.trim(),s=a.querySelector("#pl-mobile").value.replace(/\D/g,""),c=a.querySelector("#pl-email").value.trim(),k=a.querySelector("#pl-cerr");if(p.length<2)return k.textContent="Please enter your name";if(s.length!==10)return k.textContent="Enter a valid 10-digit mobile number";t.customer={name:p,mobile:s,email:c},d("options")};else if(e==="options"){a.querySelectorAll(".pl-navitem").forEach(s=>s.onclick=()=>{s.dataset.m!=="upi"&&L("Coming soon \u2014 please pay via UPI / QR for now")});let p=a.querySelector("#pl-verify");p&&(p.onclick=()=>{let s=(a.querySelector("#pl-vpa").value||"").trim();if(!/^[\w.\-]{2,}@[a-zA-Z]{2,}$/.test(s))return a.querySelector("#pl-vpa").focus(),L("Enter a valid UPI ID, e.g. name@okhdfcbank");let c=t.order&&(t.order.upi_intent_url||t.order.payment_link);c&&/Android|iPhone|iPad|Mobile/i.test(navigator.userAgent)?location.href=c:L("Scan the QR with your phone\u2019s UPI app to complete the payment.")}),A(),w(!0)}}let b=!1;async function w(e){if(e||d("options"),t.order){$();return}if(!b){b=!0;try{t.customer||(t.customer={name:l.prefill.name||"Customer",mobile:(l.prefill.contact||"").replace(/\D/g,""),email:l.prefill.email||""}),t.order=await l.createOrder(t.customer),$(),X()}catch(a){let p=o.querySelector("#pl-qr");if(p){p.innerHTML=`<div class="pl-qrerr"><span>${y(a.message||"Could not start payment")}</span><button class="pl-retry" id="pl-retry">Try again</button></div>`;let s=p.querySelector("#pl-retry");s&&(s.onclick=()=>{b=!1,p.innerHTML='<div class="pl-qrloading"></div>',w(!0)})}}finally{b=!1}}}function $(){let e=t.order;if(!e)return;Q(e);let a=o.querySelector("#pl-qr");a&&(e.qr_data_url?a.innerHTML=`<img src="${e.qr_data_url}" alt="UPI QR">`:a.innerHTML=`<div class="pl-qrerr"><span>${y(e.qr_unavailable_reason||"QR unavailable \u2014 use a UPI app below")}</span></div>`);let p=e.upi_intent_url||e.payment_link||"#",s=o.querySelector("#pl-applist");s&&s.querySelectorAll(".pl-uapp").forEach(c=>{c.setAttribute("href",p),c.setAttribute("target","_blank"),c.setAttribute("rel","noopener")}),e.test_mode&&typeof e.simulate=="function"&&V(e),A()}function V(e){let a=o.querySelector(".pl-scan");a&&(a.textContent="Test mode \u2014 no real payment. Simulate the result below.");let p=o.querySelector("#pl-applist");p&&(p.style.display="none");let s=o.querySelector(".pl-paneh2");s&&(s.textContent="Simulate payment (test mode)");let c=o.querySelector(".pl-vparow");!c||c.dataset.sim||(c.dataset.sim="1",c.innerHTML='<div class="pl-simrow"><button class="pl-sim pl-sim-ok" data-outcome="success">Simulate success</button><button class="pl-sim pl-sim-fail" data-outcome="failure">Simulate failure</button></div>',c.querySelectorAll(".pl-sim").forEach(k=>k.onclick=async()=>{c.querySelectorAll(".pl-sim").forEach(P=>P.disabled=!0),z("pending","Simulating\u2026");try{await e.simulate(k.dataset.outcome)}catch(P){z("failed",P.message||"Simulation failed"),c.querySelectorAll(".pl-sim").forEach(U=>U.disabled=!1)}}))}function Q(e){e&&e.expires_at&&e.server_now?t.expiryMs=Date.now()+Math.max(0,e.expires_at-e.server_now):t.expiryMs||(t.expiryMs=Date.now()+5*60*1e3)}function B(){if(t.done)return;let e=o.querySelector("#pl-qr");e&&(e.innerHTML='<div class="pl-qrerr"><span>QR code expired</span><button class="pl-retry" id="pl-regen">Generate new QR</button></div>');let a=o.querySelector("#pl-regen");a&&(a.onclick=F);let p=o.querySelector("#pl-timer");p&&(p.textContent="Expired");let s=o.querySelector(".pl-validrow");s&&s.classList.add("pl-expired")}function F(){if(t.stop){try{t.stop()}catch{}t.stop=null}t.order=null,t.expiryMs=null,b=!1;let e=o.querySelector(".pl-validrow");e&&e.classList.remove("pl-expired");let a=o.querySelector("#pl-qr");a&&(a.innerHTML='<div class="pl-qrloading"></div>'),w(!0)}function X(){!t.order||t.stop||(t.stop=l.watchOrder(t.order.merchant_order_no,e=>{!e||typeof e.status!="number"||(e.status===0?(t.utr=e.utr||t.utr,G()):e.status===3?(clearInterval(t.timer),t.expiryMs=Date.now(),B(),z("failed","QR expired \u2014 generate a new one")):[1,4].includes(e.status)?z("failed","Payment "+(e.status_label||"failed").toLowerCase()):e.status===6&&z("pending","Processing payment\u2026"))}))}function z(e,a){let p=o.querySelector("#pl-status");p&&(p.className=e==="failed"?"pl-pendrow pl-fail":"pl-pendrow",p.innerHTML=(e==="pending"?'<span class="pl-mini-spin"></span> ':"")+y(a))}function G(){t.done||(t.done=!0,clearInterval(t.timer),d("success"),l.onSuccess&&l.onSuccess({merchant_order_no:t.order.merchant_order_no,utr:t.utr,amount:l.amount}),t.utr||W())}async function W(){if(typeof l.fetchUtr=="function")for(let e=0;e<10&&!t.utr;e++){await new Promise(a=>setTimeout(a,4e3));try{let a=await l.fetchUtr();if(a){t.utr=a;let p=o.querySelector("#pl-pid");p&&t.screen==="success"&&p.insertAdjacentHTML("afterend",`<div class="pl-pid pl-utr">UTR ${y(a)}</div>`);break}}catch{}}}function A(){clearInterval(t.timer);let e=()=>{let a=o.querySelector("#pl-timer");if(!a)return;if(!t.expiryMs){a.textContent="Valid 5:00";return}let p=Math.max(0,Math.round((t.expiryMs-Date.now())/1e3));a.textContent=p>0?"Valid "+se(p):"Expired",p<=0&&(clearInterval(t.timer),B())};e(),t.timer=setInterval(e,1e3)}function L(e){let a=document.getElementById("pl-toast");a||(a=document.createElement("div"),a.id="pl-toast",document.body.appendChild(a)),a.textContent=e,a.classList.add("pl-show"),clearTimeout(a._t),a._t=setTimeout(()=>a.classList.remove("pl-show"),2600)}return d("loader"),setTimeout(()=>{let e=l.prefill||{};e.name&&(e.contact||"").replace(/\D/g,"").length===10?(t.customer={name:e.name,mobile:e.contact.replace(/\D/g,""),email:e.email||""},d("options")):d("contact");let a=o.querySelector("#pl-splash");a&&(a.classList.add("pl-splash-hide"),setTimeout(()=>a.remove(),450))},1600),{close:q}}var Y={apiBase:"https://payulink.io/api",assetBase:"https://assetcdn.payulink.io/checkout/v1/assets",name:null,logo:null,brandColor:null,pollMs:2500},M=class l{constructor(i={}){if(typeof window=="undefined"||typeof document=="undefined")throw new Error("PayuLink checkout runs in the browser only. On React Native use @thepayulink/checkout-react-native; on a server use @thepayulink/server.");let r=i.key||i.keyId||i.key_id;if(!r)throw new Error("PayuLink: `key` (your publishable pl_live_\u2026 / pl_test_\u2026 key) is required");if(/^pl_(live|test)_/.test(r)||console.warn("PayuLink: `key` should look like pl_live_\u2026 or pl_test_\u2026"),i.keySecret||i.key_secret)throw new Error("PayuLink: never pass a key_secret to the browser SDK. It belongs on your server only.");this.options=Object.assign({},Y,i,{key:r})}open(i={}){let r=Object.assign({},this.options,i),m=String(r.apiBase||"").replace(/\/$/,""),h=String(r.assetBase||`${m}/assets`).replace(/\/$/,""),t=r.order_id||r.orderId;if(!t)throw new Error("PayuLink: `order_id` is required. Create it on your server with POST /v1/orders.");let o=f=>{typeof r.onError=="function"?r.onError(f):console.error("PayuLink:",f.message||f)},v=null,E=null,I=async()=>v!=null&&v.session_token?(await fetch(`${m}/v1/checkout/session/${encodeURIComponent(v.session_token)}`)).json().catch(()=>({})):{},j=async f=>{var g,u,T;let d=await fetch(`${m}/v1/checkout/session`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key_id:r.key,order_id:t,customer:f?{name:f.name,contact:f.mobile||f.contact,email:f.email}:void 0})}),n=await d.json().catch(()=>({}));if(!d.ok){let _=new Error(((g=n==null?void 0:n.error)==null?void 0:g.description)||`Could not start payment (HTTP ${d.status})`);throw _.code=(u=n==null?void 0:n.error)==null?void 0:u.code,_.retriable=((T=n==null?void 0:n.error)==null?void 0:T.code)==="GATEWAY_BUSY",_}return v=n,{merchant_order_no:n.order_id,amount:n.amount/100,upi_intent_url:n.upi_intent_url,payment_link:n.payment_link,qr_data_url:n.qr_data_url||null,qr_unavailable_reason:n.qr_unavailable_reason||null,qr_retriable:n.qr_retriable===!0,expires_at:n.expires_at?n.expires_at*1e3:null,server_now:Date.now(),test_mode:n.test_mode===!0,simulate:n.test_mode===!0&&n.simulate_url?async _=>{var $;let b=await fetch(`${m}${n.simulate_url}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({outcome:_})}),w=await b.json().catch(()=>({}));if(!b.ok)throw new Error((($=w==null?void 0:w.error)==null?void 0:$.description)||`Simulation failed (HTTP ${b.status})`);return w}:null}},C=(f,d)=>{let n=async()=>{if(v!=null&&v.session_token)try{let u=await I();u.status==="paid"&&u.payulink_signature&&(E={payulink_order_id:u.payulink_order_id,payulink_payment_id:u.payulink_payment_id,payulink_signature:u.payulink_signature,utr:u.utr||null,amount:u.amount}),d({status:u.status==="paid"?0:u.status==="failed"?1:u.status==="expired"?3:2,status_label:String(u.status||"pending").toUpperCase(),utr:u.utr||null})}catch{}},g=setInterval(n,r.pollMs||Y.pollMs);return n(),()=>clearInterval(g)},q={_inner:null,close(){var f;(f=this._inner)==null||f.close()}};return fetch(`${m}/v1/checkout/order/${encodeURIComponent(t)}?key_id=${encodeURIComponent(r.key)}`).then(async f=>{var n;let d=await f.json().catch(()=>({}));if(!f.ok)throw new Error(((n=d==null?void 0:d.error)==null?void 0:n.description)||"Unknown or expired order");q._inner=H(Object.assign({},{apiBase:m,assetBase:h,name:r.name||d.merchant_name,logo:r.logo||`${h}/payulink-logo.png`,brandColor:r.brandColor||r.theme&&r.theme.color||null,testMode:d.mode==="test",note:r.note,prefill:r.prefill||{},amount:d.amount/100,description:r.description||r.item||d.merchant_name,createOrder:j,watchOrder:C,fetchUtr:async()=>(await I()).utr||null,onSuccess:()=>{let g=E||{payulink_order_id:(v==null?void 0:v.order_id)||t,payulink_payment_id:null,payulink_signature:null};typeof r.handler=="function"&&r.handler(g),typeof r.onSuccess=="function"&&r.onSuccess(g)},onDismiss:()=>{typeof r.onDismiss=="function"&&r.onDismiss()},onError:o}))}).catch(o),q}static open(i={}){return new l(i).open(i)}};typeof window!="undefined"&&(window.PayuLink=M,window.PayulinkCheckout=M);return oe(ce);})();
|
|
339
350
|
window.PayulinkCheckout=PayulinkCheckoutNS.default||PayulinkCheckoutNS;
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -69,6 +69,12 @@ export default class PayuLink {
|
|
|
69
69
|
let session = null; // the open session (carries session_token)
|
|
70
70
|
let handoff = null; // captured from the poller — the ONLY source of the signature
|
|
71
71
|
|
|
72
|
+
const pollSession = async () => {
|
|
73
|
+
if (!session?.session_token) return {};
|
|
74
|
+
const r = await fetch(`${apiBase}/v1/checkout/session/${encodeURIComponent(session.session_token)}`);
|
|
75
|
+
return r.json().catch(() => ({}));
|
|
76
|
+
};
|
|
77
|
+
|
|
72
78
|
// 1. Open a session with the PUBLISHABLE key. No secret, ever.
|
|
73
79
|
// The modal calls this once it has the customer's contact details.
|
|
74
80
|
const createOrder = async (customer) => {
|
|
@@ -118,6 +124,21 @@ export default class PayuLink {
|
|
|
118
124
|
// 1800ms (~2s) — the QR looked dead on arrival.
|
|
119
125
|
expires_at: s.expires_at ? s.expires_at * 1000 : null,
|
|
120
126
|
server_now: Date.now(),
|
|
127
|
+
// pl_test_ orders: nothing to pay. The modal swaps the UPI-ID row for buttons that
|
|
128
|
+
// settle the order via the backend's simulate endpoint (test orders only).
|
|
129
|
+
test_mode: s.test_mode === true,
|
|
130
|
+
simulate: s.test_mode === true && s.simulate_url
|
|
131
|
+
? async (outcome) => {
|
|
132
|
+
const r = await fetch(`${apiBase}${s.simulate_url}`, {
|
|
133
|
+
method: 'POST',
|
|
134
|
+
headers: { 'Content-Type': 'application/json' },
|
|
135
|
+
body: JSON.stringify({ outcome }),
|
|
136
|
+
});
|
|
137
|
+
const out = await r.json().catch(() => ({}));
|
|
138
|
+
if (!r.ok) throw new Error(out?.error?.description || `Simulation failed (HTTP ${r.status})`);
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
: null,
|
|
121
142
|
};
|
|
122
143
|
};
|
|
123
144
|
|
|
@@ -127,8 +148,7 @@ export default class PayuLink {
|
|
|
127
148
|
const tick = async () => {
|
|
128
149
|
if (!session?.session_token) return;
|
|
129
150
|
try {
|
|
130
|
-
const
|
|
131
|
-
const s = await r.json().catch(() => ({}));
|
|
151
|
+
const s = await pollSession();
|
|
132
152
|
if (s.status === 'paid' && s.payulink_signature) {
|
|
133
153
|
handoff = {
|
|
134
154
|
payulink_order_id: s.payulink_order_id,
|
|
@@ -166,13 +186,18 @@ export default class PayuLink {
|
|
|
166
186
|
assetBase,
|
|
167
187
|
name: o.name || meta.merchant_name,
|
|
168
188
|
logo: o.logo || `${assetBase}/payulink-logo.png`,
|
|
169
|
-
brandColor
|
|
189
|
+
// `theme.color` is the documented option; `brandColor` the older name. Both work.
|
|
190
|
+
brandColor: o.brandColor || (o.theme && o.theme.color) || null,
|
|
191
|
+
testMode: meta.mode === 'test',
|
|
170
192
|
note: o.note,
|
|
171
193
|
prefill: o.prefill || {},
|
|
172
194
|
amount: meta.amount / 100, // PAISE -> rupees for the modal's formatter
|
|
173
195
|
description: o.description || o.item || meta.merchant_name,
|
|
174
196
|
createOrder,
|
|
175
197
|
watchOrder,
|
|
198
|
+
// UTR fallback after success: read it from this session. (This used to call
|
|
199
|
+
// `${apiBase}/api/order/…` — apiBase already ends in /api, so it never resolved.)
|
|
200
|
+
fetchUtr: async () => (await pollSession()).utr || null,
|
|
176
201
|
onSuccess: () => {
|
|
177
202
|
const result = handoff || {
|
|
178
203
|
payulink_order_id: session?.order_id || orderId,
|
package/src/modal.js
CHANGED
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
// Adapted from Elite Yatra's hosted PayulinkCheckout widget.
|
|
3
3
|
import { injectStyles } from './style.js';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// ₹199.50, ₹200 — two decimals whenever there are paise, none for whole rupees.
|
|
6
|
+
const inr = (r) => {
|
|
7
|
+
const n = Number(r || 0);
|
|
8
|
+
return '₹' + n.toLocaleString('en-IN', { minimumFractionDigits: n % 1 === 0 ? 0 : 2, maximumFractionDigits: 2 });
|
|
9
|
+
};
|
|
6
10
|
const mmss = (s) => `${Math.floor(s / 60)}:${(s % 60) < 10 ? '0' : ''}${s % 60}`;
|
|
7
11
|
const esc = (s) => String(s == null ? '' : s).replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c]));
|
|
8
12
|
|
|
@@ -69,6 +73,7 @@ export function openModal(o) {
|
|
|
69
73
|
</div>
|
|
70
74
|
</div>
|
|
71
75
|
<div class="pl-summary">
|
|
76
|
+
${o.testMode ? '<div class="pl-testnote">TEST MODE · no real payment</div>' : ''}
|
|
72
77
|
<div class="pl-sl">Price Summary</div>
|
|
73
78
|
<div class="pl-samt">${amt}</div>
|
|
74
79
|
<div class="pl-snote">${esc(o.note || ('Paying for ' + (o.description || o.name)))}</div>
|
|
@@ -80,6 +85,7 @@ export function openModal(o) {
|
|
|
80
85
|
<header class="pl-head">
|
|
81
86
|
<button class="pl-hic pl-back" id="pl-back" aria-label="Back">${IC.back}</button>
|
|
82
87
|
<div class="pl-title" id="pl-title">Payment Options</div>
|
|
88
|
+
${o.testMode ? '<span class="pl-testbadge" title="Test mode — no real money moves">TEST</span>' : ''}
|
|
83
89
|
<button class="pl-hic pl-dots" aria-label="More">⋯</button>
|
|
84
90
|
<button class="pl-hic pl-close-x" id="pl-close" aria-label="Close">×</button>
|
|
85
91
|
</header>
|
|
@@ -272,9 +278,30 @@ export function openModal(o) {
|
|
|
272
278
|
const intent = ord.upi_intent_url || ord.payment_link || '#';
|
|
273
279
|
const list = root.querySelector('#pl-applist');
|
|
274
280
|
if (list) list.querySelectorAll('.pl-uapp').forEach((a) => { a.setAttribute('href', intent); a.setAttribute('target', '_blank'); a.setAttribute('rel', 'noopener'); });
|
|
281
|
+
if (ord.test_mode && typeof ord.simulate === 'function') renderSimulate(ord);
|
|
275
282
|
startTimer();
|
|
276
283
|
}
|
|
277
284
|
|
|
285
|
+
// Test mode: there is no UPI payment to make, so offer the two outcomes instead.
|
|
286
|
+
// The result still arrives through the normal session poll, exactly like a live payment.
|
|
287
|
+
function renderSimulate(ord) {
|
|
288
|
+
const scan = root.querySelector('.pl-scan');
|
|
289
|
+
if (scan) scan.textContent = 'Test mode — no real payment. Simulate the result below.';
|
|
290
|
+
const apps = root.querySelector('#pl-applist'); if (apps) apps.style.display = 'none';
|
|
291
|
+
const h2 = root.querySelector('.pl-paneh2'); if (h2) h2.textContent = 'Simulate payment (test mode)';
|
|
292
|
+
const row = root.querySelector('.pl-vparow');
|
|
293
|
+
if (!row || row.dataset.sim) return;
|
|
294
|
+
row.dataset.sim = '1';
|
|
295
|
+
row.innerHTML = `<div class="pl-simrow"><button class="pl-sim pl-sim-ok" data-outcome="success">Simulate success</button>`
|
|
296
|
+
+ `<button class="pl-sim pl-sim-fail" data-outcome="failure">Simulate failure</button></div>`;
|
|
297
|
+
row.querySelectorAll('.pl-sim').forEach((b) => (b.onclick = async () => {
|
|
298
|
+
row.querySelectorAll('.pl-sim').forEach((x) => (x.disabled = true));
|
|
299
|
+
setStatus('pending', 'Simulating…');
|
|
300
|
+
try { await ord.simulate(b.dataset.outcome); }
|
|
301
|
+
catch (e) { setStatus('failed', e.message || 'Simulation failed'); row.querySelectorAll('.pl-sim').forEach((x) => (x.disabled = false)); }
|
|
302
|
+
}));
|
|
303
|
+
}
|
|
304
|
+
|
|
278
305
|
function setExpiryFromOrder(ord) {
|
|
279
306
|
if (ord && ord.expires_at && ord.server_now) {
|
|
280
307
|
state.expiryMs = Date.now() + Math.max(0, ord.expires_at - ord.server_now);
|
|
@@ -322,11 +349,12 @@ export function openModal(o) {
|
|
|
322
349
|
if (!state.utr) pollUtr();
|
|
323
350
|
}
|
|
324
351
|
async function pollUtr() {
|
|
352
|
+
if (typeof o.fetchUtr !== 'function') return;
|
|
325
353
|
for (let i = 0; i < 10 && !state.utr; i++) {
|
|
326
354
|
await new Promise((r) => setTimeout(r, 4000));
|
|
327
355
|
try {
|
|
328
|
-
const
|
|
329
|
-
if (
|
|
356
|
+
const utr = await o.fetchUtr();
|
|
357
|
+
if (utr) { state.utr = utr; const pid = root.querySelector('#pl-pid'); if (pid && state.screen === 'success') pid.insertAdjacentHTML('afterend', `<div class="pl-pid pl-utr">UTR ${esc(utr)}</div>`); break; }
|
|
330
358
|
} catch {}
|
|
331
359
|
}
|
|
332
360
|
}
|
package/src/style.js
CHANGED
|
@@ -175,6 +175,15 @@ export const CSS = `
|
|
|
175
175
|
#pl-overlay .pl-vparow { display: flex; flex-direction: column; gap: 12px; border: 1px solid #e6ebf4; border-radius: 14px; padding: 16px; }
|
|
176
176
|
#pl-overlay .pl-vpa { width: 100%; padding: 14px 14px; border: 1px solid #d5dae0; border-radius: 10px; font-size: 15px; font-family: inherit; color: var(--pl-ink); outline: none; transition: border-color .15s, box-shadow .15s; }
|
|
177
177
|
#pl-overlay .pl-vpa:focus { border-color: var(--pl-blue); box-shadow: 0 0 0 3px rgba(51,149,255,.16); }
|
|
178
|
+
#pl-overlay .pl-testbadge { font-size: 10.5px; font-weight: 800; letter-spacing: .6px; color: #92400e; background: #fef3c7; border: 1px solid #f59e0b; padding: 3px 8px; border-radius: 999px; flex-shrink: 0; }
|
|
179
|
+
#pl-overlay .pl-testnote { font-size: 11px; font-weight: 800; letter-spacing: .5px; color: #92400e; background: #fef3c7; border: 1px dashed #f59e0b; padding: 5px 8px; border-radius: 8px; margin-bottom: 10px; text-align: center; }
|
|
180
|
+
#pl-overlay .pl-simrow { display: flex; gap: 10px; }
|
|
181
|
+
#pl-overlay .pl-sim { flex: 1; padding: 13px 10px; border-radius: 10px; font-size: 14px; font-weight: 700; font-family: inherit; cursor: pointer; border: 1px solid transparent; transition: filter .15s, opacity .15s; }
|
|
182
|
+
#pl-overlay .pl-sim:disabled { opacity: .55; cursor: default; }
|
|
183
|
+
#pl-overlay .pl-sim-ok { background: #16a34a; color: #fff; }
|
|
184
|
+
#pl-overlay .pl-sim-fail { background: #fff; color: #b91c1c; border-color: #fca5a5; }
|
|
185
|
+
#pl-overlay .pl-sim:not(:disabled):hover { filter: brightness(.95); }
|
|
186
|
+
@media (max-width: 840px) { #pl-overlay .pl-testnote { display: none; } }
|
|
178
187
|
#pl-overlay .pl-vpa::placeholder { color: #aab2bf; }
|
|
179
188
|
#pl-overlay .pl-verify { width: 100%; padding: 15px; background: #16181f; color: #fff; border: none; border-radius: 10px; font-size: 15px; font-weight: 700; cursor: pointer; font-family: inherit; transition: filter .15s, transform .08s; }
|
|
180
189
|
#pl-overlay .pl-verify:hover { filter: brightness(1.25); }
|