@payinto/checkout-sdk 1.4.3 → 1.4.4
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 +60 -5
- package/dist/v1/checkout.es.js +1 -1
- package/dist/v1/checkout.js +1 -1
- package/dist/v1/types/checkout-sdk.d.ts +22 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -73,16 +73,16 @@ window.PayintoCheckout.open({
|
|
|
73
73
|
| `onClose` | `function`| No | Callback when the checkout is closed. |
|
|
74
74
|
| `onError` | `function`| No | Callback on error. |
|
|
75
75
|
|
|
76
|
-
##
|
|
76
|
+
## Browser Callback Payloads
|
|
77
77
|
|
|
78
78
|
### Success
|
|
79
79
|
```ts
|
|
80
80
|
{
|
|
81
|
-
type: 'checkout.
|
|
81
|
+
type: 'checkout.succeeded',
|
|
82
82
|
status: 'success',
|
|
83
|
-
reference
|
|
84
|
-
merchant_reference
|
|
85
|
-
token
|
|
83
|
+
reference?: string,
|
|
84
|
+
merchant_reference?: string,
|
|
85
|
+
token?: string
|
|
86
86
|
}
|
|
87
87
|
```
|
|
88
88
|
|
|
@@ -95,6 +95,61 @@ window.PayintoCheckout.open({
|
|
|
95
95
|
}
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
## Server payment confirmation and webhooks
|
|
99
|
+
|
|
100
|
+
The browser success callback uses `checkout.succeeded`. It is a UI notification, not a signed server webhook. Keep using the `onSuccess` callback option; its payload `type` is now `checkout.succeeded`. The SDK accepts legacy `checkout.success` messages from older checkout frames and normalizes them to the new payload. Update any code that compares `payload.type`, and deploy the updated SDK and checkout app together. Do not fulfill orders solely from browser messages or redirects.
|
|
101
|
+
|
|
102
|
+
Subscribe your backend to **`checkout.payment.succeeded`**. Verified bank-transfer and card checkout payments use the same event, distinguished by `data.payment_method` (`bank_transfer` or `card`). Future methods such as USSD will use the event once their verified confirmation flow is integrated; this SDK change does not enable them.
|
|
103
|
+
|
|
104
|
+
| Server event | Trigger |
|
|
105
|
+
| --- | --- |
|
|
106
|
+
| `checkout.payment.succeeded` | Verified checkout payment succeeds; use for checkout fulfillment. |
|
|
107
|
+
| `virtual_account.transfer.received` | A virtual-account deposit is credited, including checkout bank transfers. |
|
|
108
|
+
| `transaction.credit.succeeded` | An account credit is recorded. |
|
|
109
|
+
| `transaction.debit.succeeded` | An account debit is recorded; external payment completion is not implied. |
|
|
110
|
+
|
|
111
|
+
A checkout bank transfer can emit the first three events. Fulfill from the checkout event only and deduplicate by `data.id` or `data.reference`. Checkout pending, failed, expired, and refunded webhook events are not currently emitted.
|
|
112
|
+
|
|
113
|
+
Every webhook has `event`, `id`, `created_at`, `api_version`, and `data`. All four include nullable `data.merchant_reference`. Checkout success also includes `payment_method`, `currency`, `payment_status: "successful"`, `paid_at`, `customer`, `metadata`, and the amount breakdown:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"event": "checkout.payment.succeeded",
|
|
118
|
+
"id": "webhook_001",
|
|
119
|
+
"created_at": "2026-10-22T12:00:00.000000Z",
|
|
120
|
+
"api_version": "2026-05-27",
|
|
121
|
+
"data": {
|
|
122
|
+
"id": "payment_inflow_001",
|
|
123
|
+
"reference": "PIF-1",
|
|
124
|
+
"merchant_reference": "ORDER-1",
|
|
125
|
+
"payment_method": "bank_transfer",
|
|
126
|
+
"currency": "NGN",
|
|
127
|
+
"base_amount": 150000,
|
|
128
|
+
"customer_charge_amount": 2000,
|
|
129
|
+
"customer_fee_amount": 2000,
|
|
130
|
+
"amount": 152000,
|
|
131
|
+
"charges_applied": true,
|
|
132
|
+
"fee": 2000,
|
|
133
|
+
"vat": 0,
|
|
134
|
+
"stamp_duty": 0,
|
|
135
|
+
"total_fee": 2000,
|
|
136
|
+
"settled_amount": 150000,
|
|
137
|
+
"payment_status": "successful",
|
|
138
|
+
"paid_at": "2026-10-22T12:00:00.000000Z",
|
|
139
|
+
"customer": null,
|
|
140
|
+
"metadata": null
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Amounts are minor currency units. `base_amount` is the order amount; `amount` includes customer-paid charges; `settled_amount` is after fees. Match the merchant reference, currency, and expected amount server-side before fulfilling once.
|
|
146
|
+
|
|
147
|
+
Verify `X-Payinto-Signature` using the webhook secret and HMAC-SHA256 over `{timestamp}.{raw_request_body}`. The header format is `t={timestamp},v1={digest}`. Use the Node or PHP server SDK to verify the exact raw body, enforce timestamp freshness, and acknowledge only after durable processing or queuing. Keep webhook secrets and API secret keys on your server.
|
|
148
|
+
|
|
149
|
+
For authenticated status checks, call `GET /api/v1/checkout/tsq/{reference}` from your backend with its secret key. This returns `data.merchant_reference` from the payment inflow or cached checkout, and `data.payment_status` (`pending`, `successful`, `failed`, `cancelled`, or `expired`). The exported `CheckoutTransactionStatusData` type describes this data; the browser SDK does not make secret-key TSQ requests.
|
|
150
|
+
|
|
151
|
+
Account transaction TSQ (`GET /api/v1/business/transactions/tsq/{reference}`) uses `data.status` and currently reads merchant references only from transaction metadata. Transaction webhooks have additional linked-source fallbacks, so their merchant reference can be populated while transaction TSQ returns null.
|
|
152
|
+
|
|
98
153
|
## Security
|
|
99
154
|
- The SDK validates message origins against an internal allowlist.
|
|
100
155
|
- It uses a secure iframe to isolate the payment process.
|
package/dist/v1/checkout.es.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e="undefined"!=typeof window,t=e?"https://checkout.payinto.co":"",o="/v1".replace(/\/+$/,""),i=`${t}${o}/popup`,n="https://checkout.payinto.co,https://go.payinto.co".split(",").map(e=>e.trim()).filter(Boolean),r=e?new URL(i).origin:"",a=/* @__PURE__ */new Set([r,...n]),c=e=>{try{const t=new URL(e.checkout_url),o=e.checkout_version||(e=>{try{const t=new URL(e).pathname.split("/").filter(Boolean)[0];return/^v[0-9]+$/i.test(t||"")?t:void 0}catch{return}})(e.checkout_url);return o?`${t.origin}/${o}/popup`:`${t.origin}/popup`}catch{return i}};const s=new class{container=null;iframe=null;messageListener=null;callbacks={};initRetryTimer=null;iframeLoadTimer=null;pendingInit=null;open(e){const t=(e=>{if("token"===e.mode)return e;if("public_key"===e.mode)return e;throw new Error('Payinto Checkout: invalid options. Provide a valid mode ("token" or "public_key").')})(e);this.container&&this.close(),this.callbacks={onClose:t.onClose,onSuccess:t.onSuccess,onError:t.onError,callback:t.callback},this.mountOverlay();(async()=>{try{const e="token"===t.mode?this.validateTokenMode(t):await this.initializePublicKeyMode(t);this.bindMessageListener(),this.initializeIframe(e)}catch(e){const t=e instanceof Error?e.message:"Unable to initialize checkout.";this.handleError({type:"checkout.error",code:"initialization_failed",message:t})}})()}pay(e){this.open(e)}close=()=>{this.clearInitRetry(),this.clearIframeLoadTimer(),this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null),this.container&&document.body.contains(this.container)&&document.body.removeChild(this.container),this.container=null,this.iframe=null,this.pendingInit=null,document.body.classList.remove("payinto-checkout-open")};clearInitRetry(){null!==this.initRetryTimer&&(window.clearInterval(this.initRetryTimer),this.initRetryTimer=null)}clearIframeLoadTimer(){null!==this.iframeLoadTimer&&(window.clearTimeout(this.iframeLoadTimer),this.iframeLoadTimer=null)}mountOverlay(){const e=document.createElement("div");e.id="payinto-checkout-container",e.style.position="fixed",e.style.top="0",e.style.left="0",e.style.right="0",e.style.bottom="0",e.style.width="100%",e.style.minHeight="100vh",e.style.height="100%",e.style.backgroundColor="rgba(0, 0, 0, 0.85)",e.style.zIndex="9999",e.style.display="flex",e.style.alignItems="flex-start",e.style.justifyContent="center",e.style.overflowY="auto",e.style.paddingTop="6rem",e.style.paddingBottom="2rem",e.style.boxSizing="border-box";const t=document.createElement("div");t.className="payinto-preloader",t.style.display="flex",t.style.flexDirection="column",t.style.alignItems="center",t.style.justifyContent="center",t.style.position="absolute",t.style.top="6rem",t.style.left="50%",t.style.transform="translateX(-50%)",t.style.width="100%",t.style.maxWidth="720px",t.style.padding="4rem 0",t.style.zIndex="10",t.style.fontFamily="system-ui, -apple-system, sans-serif";const o=document.createElement("div");o.style.width="40px",o.style.height="40px",o.style.borderRadius="50%",o.style.border="2px solid transparent",o.style.borderBottom="2px solid #ffffff",o.style.marginBottom="16px",o.style.animation="spin 1s linear infinite";const i=document.createElement("p");i.id="payinto-checkout-subtitle",i.innerText="Initializing Secure Checkout...",i.style.fontSize="12px",i.style.color="#e5e7eb",i.style.margin="0",t.appendChild(o),t.appendChild(i),e.appendChild(t);const n=document.createElement("iframe");if(n.style.width="100%",n.style.maxWidth="720px",n.style.height="auto",n.style.border="none",n.style.backgroundColor="transparent",n.style.opacity="0",n.style.transition="opacity 0.25s ease",n.allow="payment; clipboard-read; clipboard-write",e.appendChild(n),!document.getElementById("payinto-checkout-styles")){const e=document.createElement("style");e.id="payinto-checkout-styles",e.innerHTML="\n @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }\n body.payinto-checkout-open { overflow: hidden !important; }\n @media (max-width: 767px) {\n #payinto-checkout-container {\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n right: 0 !important;\n bottom: 0 !important;\n height: 100% !important;\n min-height: 100vh !important;\n overflow-y: auto !important;\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .payinto-preloader { top: 2rem !important; }\n }\n ",document.head.appendChild(e)}document.body.appendChild(e),document.body.classList.add("payinto-checkout-open"),this.container=e,this.iframe=n}validateTokenMode(e){if(!e.token||"string"!=typeof e.token)throw new Error('Payinto Checkout: token is required for mode="token".');if(!e.checkout_url||"string"!=typeof e.checkout_url)throw new Error('Payinto Checkout: checkout_url is required for mode="token".');if(!new URL(e.checkout_url).origin)throw new Error("Payinto Checkout: invalid checkout_url.");return{token:e.token,checkout_url:e.checkout_url,merchant_reference:e.merchant_reference,checkout_version:e.checkout_version}}async initializePublicKeyMode(e){if(!e.public_key)throw new Error('Payinto Checkout: public_key is required for mode="public_key".');if(!e.customer?.email)throw new Error('Payinto Checkout: customer.email is required for mode="public_key".');if(!e.amount||e.amount<=0)throw new Error('Payinto Checkout: amount must be greater than zero for mode="public_key".');const t={merchant_reference:e.merchant_reference,amount:e.amount,currency:e.currency,customer:e.customer,redirect_url:e.redirect_url,metadata:e.metadata,settlement_account_id:e.settlement_account_id,checkout_version:e.checkout_version},o=new AbortController,i=window.setTimeout(()=>o.abort(),2e4);let n;try{n=await fetch("https://api.payinto.co/api/v1/checkout/initialize",{method:"POST",cache:"no-store",headers:{"Content-Type":"application/json",Accept:"application/json",Authorization:`Bearer ${e.public_key}`},body:JSON.stringify(t),signal:o.signal})}catch(c){if(c instanceof DOMException&&"AbortError"===c.name)throw new Error("Checkout initialization timed out. Please try again.");throw new Error("Unable to connect to checkout. Please check your connection and try again.")}finally{window.clearTimeout(i)}if(!n.ok){const e=(e=>{try{return JSON.parse(e)}catch{return null}})(await n.text());throw new Error(e?.message||"Failed to initialize transaction.")}const r=await n.json(),a=r.data||(r.token&&r.checkout_url?{token:r.token,checkout_url:r.checkout_url,merchant_reference:r.merchant_reference,checkout_version:r.checkout_version}:void 0);if(!a?.token||!a?.checkout_url)throw new Error("Payinto Checkout: invalid initialize response.");return{token:a.token,checkout_url:a.checkout_url,merchant_reference:a.merchant_reference,checkout_version:a.checkout_version}}initializeIframe(e){if(!this.iframe||!this.container)return;const t=this.container.querySelector("#payinto-checkout-subtitle"),o=this.container.querySelector(".payinto-preloader"),i=c(e),n=new URL(i).origin,r=new URL(e.checkout_url).origin,s=Array.from(/* @__PURE__ */new Set([n,r]));if(!a.has(n)&&n!==new URL(e.checkout_url).origin)throw new Error("Payinto Checkout: target origin is not in allowlist.");this.iframe.src=`${i}?updated=${Date.now()}`,this.iframeLoadTimer=window.setTimeout(()=>{this.iframeLoadTimer=null,this.handleError({type:"checkout.error",code:"iframe_load_timeout",message:"Checkout took too long to load. Please try again."})},2e4),this.iframe.onload=()=>{this.clearIframeLoadTimer(),o&&(o.style.display="none"),this.iframe&&(this.iframe.style.opacity="1");const t={type:"checkout.init",token:e.token,merchant_reference:e.merchant_reference,checkout_url:e.checkout_url};this.pendingInit={payload:t,origins:s};const i=()=>{if(this.pendingInit)for(const e of this.pendingInit.origins)this.iframe?.contentWindow?.postMessage(this.pendingInit.payload,e)};i(),this.clearInitRetry();let n=0;this.initRetryTimer=window.setInterval(()=>{n+=1,n>=8?this.clearInitRetry():i()},500)},this.iframe.onerror=()=>{this.clearIframeLoadTimer(),this.handleError({type:"checkout.error",code:"iframe_load_failed",message:"Unable to load checkout. Please check your connection and try again."})},t&&(t.innerText="Loading checkout...")}bindMessageListener(){const e=e=>{if(!a.has(e.origin))return;this.clearInitRetry();const t=e.data;if("object"==typeof t&&"checkout.resize"===t?.type&&this.iframe)this.iframe.style.height=`${Math.max(300,t.height||0)}px
|
|
1
|
+
const e="undefined"!=typeof window,t=e?"https://checkout.payinto.co":"",o="/v1".replace(/\/+$/,""),i=`${t}${o}/popup`,n="https://checkout.payinto.co,https://go.payinto.co".split(",").map(e=>e.trim()).filter(Boolean),r=e?new URL(i).origin:"",a=/* @__PURE__ */new Set([r,...n]),c=e=>{try{const t=new URL(e.checkout_url),o=e.checkout_version||(e=>{try{const t=new URL(e).pathname.split("/").filter(Boolean)[0];return/^v[0-9]+$/i.test(t||"")?t:void 0}catch{return}})(e.checkout_url);return o?`${t.origin}/${o}/popup`:`${t.origin}/popup`}catch{return i}};const s=new class{container=null;iframe=null;messageListener=null;callbacks={};initRetryTimer=null;iframeLoadTimer=null;pendingInit=null;open(e){const t=(e=>{if("token"===e.mode)return e;if("public_key"===e.mode)return e;throw new Error('Payinto Checkout: invalid options. Provide a valid mode ("token" or "public_key").')})(e);this.container&&this.close(),this.callbacks={onClose:t.onClose,onSuccess:t.onSuccess,onError:t.onError,callback:t.callback},this.mountOverlay();(async()=>{try{const e="token"===t.mode?this.validateTokenMode(t):await this.initializePublicKeyMode(t);this.bindMessageListener(),this.initializeIframe(e)}catch(e){const t=e instanceof Error?e.message:"Unable to initialize checkout.";this.handleError({type:"checkout.error",code:"initialization_failed",message:t})}})()}pay(e){this.open(e)}close=()=>{this.clearInitRetry(),this.clearIframeLoadTimer(),this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null),this.container&&document.body.contains(this.container)&&document.body.removeChild(this.container),this.container=null,this.iframe=null,this.pendingInit=null,document.body.classList.remove("payinto-checkout-open")};clearInitRetry(){null!==this.initRetryTimer&&(window.clearInterval(this.initRetryTimer),this.initRetryTimer=null)}clearIframeLoadTimer(){null!==this.iframeLoadTimer&&(window.clearTimeout(this.iframeLoadTimer),this.iframeLoadTimer=null)}mountOverlay(){const e=document.createElement("div");e.id="payinto-checkout-container",e.style.position="fixed",e.style.top="0",e.style.left="0",e.style.right="0",e.style.bottom="0",e.style.width="100%",e.style.minHeight="100vh",e.style.height="100%",e.style.backgroundColor="rgba(0, 0, 0, 0.85)",e.style.zIndex="9999",e.style.display="flex",e.style.alignItems="flex-start",e.style.justifyContent="center",e.style.overflowY="auto",e.style.paddingTop="6rem",e.style.paddingBottom="2rem",e.style.boxSizing="border-box";const t=document.createElement("div");t.className="payinto-preloader",t.style.display="flex",t.style.flexDirection="column",t.style.alignItems="center",t.style.justifyContent="center",t.style.position="absolute",t.style.top="6rem",t.style.left="50%",t.style.transform="translateX(-50%)",t.style.width="100%",t.style.maxWidth="720px",t.style.padding="4rem 0",t.style.zIndex="10",t.style.fontFamily="system-ui, -apple-system, sans-serif";const o=document.createElement("div");o.style.width="40px",o.style.height="40px",o.style.borderRadius="50%",o.style.border="2px solid transparent",o.style.borderBottom="2px solid #ffffff",o.style.marginBottom="16px",o.style.animation="spin 1s linear infinite";const i=document.createElement("p");i.id="payinto-checkout-subtitle",i.innerText="Initializing Secure Checkout...",i.style.fontSize="12px",i.style.color="#e5e7eb",i.style.margin="0",t.appendChild(o),t.appendChild(i),e.appendChild(t);const n=document.createElement("iframe");if(n.style.width="100%",n.style.maxWidth="720px",n.style.height="auto",n.style.border="none",n.style.backgroundColor="transparent",n.style.opacity="0",n.style.transition="opacity 0.25s ease",n.allow="payment; clipboard-read; clipboard-write",e.appendChild(n),!document.getElementById("payinto-checkout-styles")){const e=document.createElement("style");e.id="payinto-checkout-styles",e.innerHTML="\n @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }\n body.payinto-checkout-open { overflow: hidden !important; }\n @media (max-width: 767px) {\n #payinto-checkout-container {\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n right: 0 !important;\n bottom: 0 !important;\n height: 100% !important;\n min-height: 100vh !important;\n overflow-y: auto !important;\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .payinto-preloader { top: 2rem !important; }\n }\n ",document.head.appendChild(e)}document.body.appendChild(e),document.body.classList.add("payinto-checkout-open"),this.container=e,this.iframe=n}validateTokenMode(e){if(!e.token||"string"!=typeof e.token)throw new Error('Payinto Checkout: token is required for mode="token".');if(!e.checkout_url||"string"!=typeof e.checkout_url)throw new Error('Payinto Checkout: checkout_url is required for mode="token".');if(!new URL(e.checkout_url).origin)throw new Error("Payinto Checkout: invalid checkout_url.");return{token:e.token,checkout_url:e.checkout_url,merchant_reference:e.merchant_reference,checkout_version:e.checkout_version}}async initializePublicKeyMode(e){if(!e.public_key)throw new Error('Payinto Checkout: public_key is required for mode="public_key".');if(!e.customer?.email)throw new Error('Payinto Checkout: customer.email is required for mode="public_key".');if(!e.amount||e.amount<=0)throw new Error('Payinto Checkout: amount must be greater than zero for mode="public_key".');const t={merchant_reference:e.merchant_reference,amount:e.amount,currency:e.currency,customer:e.customer,redirect_url:e.redirect_url,metadata:e.metadata,settlement_account_id:e.settlement_account_id,checkout_version:e.checkout_version},o=new AbortController,i=window.setTimeout(()=>o.abort(),2e4);let n;try{n=await fetch("https://api.payinto.co/api/v1/checkout/initialize",{method:"POST",cache:"no-store",headers:{"Content-Type":"application/json",Accept:"application/json",Authorization:`Bearer ${e.public_key}`},body:JSON.stringify(t),signal:o.signal})}catch(c){if(c instanceof DOMException&&"AbortError"===c.name)throw new Error("Checkout initialization timed out. Please try again.");throw new Error("Unable to connect to checkout. Please check your connection and try again.")}finally{window.clearTimeout(i)}if(!n.ok){const e=(e=>{try{return JSON.parse(e)}catch{return null}})(await n.text());throw new Error(e?.message||"Failed to initialize transaction.")}const r=await n.json(),a=r.data||(r.token&&r.checkout_url?{token:r.token,checkout_url:r.checkout_url,merchant_reference:r.merchant_reference,checkout_version:r.checkout_version}:void 0);if(!a?.token||!a?.checkout_url)throw new Error("Payinto Checkout: invalid initialize response.");return{token:a.token,checkout_url:a.checkout_url,merchant_reference:a.merchant_reference,checkout_version:a.checkout_version}}initializeIframe(e){if(!this.iframe||!this.container)return;const t=this.container.querySelector("#payinto-checkout-subtitle"),o=this.container.querySelector(".payinto-preloader"),i=c(e),n=new URL(i).origin,r=new URL(e.checkout_url).origin,s=Array.from(/* @__PURE__ */new Set([n,r]));if(!a.has(n)&&n!==new URL(e.checkout_url).origin)throw new Error("Payinto Checkout: target origin is not in allowlist.");this.iframe.src=`${i}?updated=${Date.now()}`,this.iframeLoadTimer=window.setTimeout(()=>{this.iframeLoadTimer=null,this.handleError({type:"checkout.error",code:"iframe_load_timeout",message:"Checkout took too long to load. Please try again."})},2e4),this.iframe.onload=()=>{this.clearIframeLoadTimer(),o&&(o.style.display="none"),this.iframe&&(this.iframe.style.opacity="1");const t={type:"checkout.init",token:e.token,merchant_reference:e.merchant_reference,checkout_url:e.checkout_url};this.pendingInit={payload:t,origins:s};const i=()=>{if(this.pendingInit)for(const e of this.pendingInit.origins)this.iframe?.contentWindow?.postMessage(this.pendingInit.payload,e)};i(),this.clearInitRetry();let n=0;this.initRetryTimer=window.setInterval(()=>{n+=1,n>=8?this.clearInitRetry():i()},500)},this.iframe.onerror=()=>{this.clearIframeLoadTimer(),this.handleError({type:"checkout.error",code:"iframe_load_failed",message:"Unable to load checkout. Please check your connection and try again."})},t&&(t.innerText="Loading checkout...")}bindMessageListener(){const e=e=>{if(!a.has(e.origin))return;this.clearInitRetry();const t=e.data;if("object"==typeof t&&"checkout.resize"===t?.type&&this.iframe)return void(this.iframe.style.height=`${Math.max(300,t.height||0)}px`);if("object"==typeof t&&"checkout.ready"===t?.type){if(this.pendingInit)for(const e of this.pendingInit.origins)this.iframe?.contentWindow?.postMessage(this.pendingInit.payload,e);return void this.clearInitRetry()}if("object"==typeof t&&"checkout.close"===t?.type)return this.callbacks.onClose?.(),void this.close();if("object"==typeof t?"checkout.succeeded"===t?.type||"checkout.success"===t?.type:"checkout.succeeded"===t||"checkout.success"===t){const e={..."object"==typeof t?t:{},type:"checkout.succeeded",status:"success"};return this.callbacks.onSuccess?.(e),this.callbacks.callback?.(e),void this.close()}"object"==typeof t&&"checkout.error"===t?.type&&this.handleError(t)};this.messageListener=e,window.addEventListener("message",e)}handleError(e){if(this.callbacks.onError?.(e),this.container){const t=this.container.querySelector(".payinto-preloader"),o=this.container.querySelector("#payinto-checkout-subtitle");t&&(t.style.display="flex"),o&&(o.innerText=e.message,o.style.color="#ff4444")}setTimeout(()=>{this.close()},1800)}},l={open:e=>s.open(e),pay:e=>s.pay(e),close:()=>s.close()},h=e=>{s.open(e)};h.open=e=>s.open(e),h.pay=e=>s.pay(e),h.close=()=>s.close(),e&&(window.PayintoCheckout=h);export{l as PayintoCheckoutApi,l as default};
|
package/dist/v1/checkout.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var PayintoCheckoutBundle=function(e){"use strict";const t="undefined"!=typeof window,o=t?"https://checkout.payinto.co":"",i="/v1".replace(/\/+$/,""),n=`${o}${i}/popup`,r="https://checkout.payinto.co,https://go.payinto.co".split(",").map(e=>e.trim()).filter(Boolean),a=t?new URL(n).origin:"",c=new Set([a,...r]),s=e=>{try{const t=new URL(e.checkout_url),o=e.checkout_version||(e=>{try{const t=new URL(e).pathname.split("/").filter(Boolean)[0];return/^v[0-9]+$/i.test(t||"")?t:void 0}catch{return}})(e.checkout_url);return o?`${t.origin}/${o}/popup`:`${t.origin}/popup`}catch{return n}};const l=new class{container=null;iframe=null;messageListener=null;callbacks={};initRetryTimer=null;iframeLoadTimer=null;pendingInit=null;open(e){const t=(e=>{if("token"===e.mode)return e;if("public_key"===e.mode)return e;throw new Error('Payinto Checkout: invalid options. Provide a valid mode ("token" or "public_key").')})(e);this.container&&this.close(),this.callbacks={onClose:t.onClose,onSuccess:t.onSuccess,onError:t.onError,callback:t.callback},this.mountOverlay();(async()=>{try{const e="token"===t.mode?this.validateTokenMode(t):await this.initializePublicKeyMode(t);this.bindMessageListener(),this.initializeIframe(e)}catch(e){const t=e instanceof Error?e.message:"Unable to initialize checkout.";this.handleError({type:"checkout.error",code:"initialization_failed",message:t})}})()}pay(e){this.open(e)}close=()=>{this.clearInitRetry(),this.clearIframeLoadTimer(),this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null),this.container&&document.body.contains(this.container)&&document.body.removeChild(this.container),this.container=null,this.iframe=null,this.pendingInit=null,document.body.classList.remove("payinto-checkout-open")};clearInitRetry(){null!==this.initRetryTimer&&(window.clearInterval(this.initRetryTimer),this.initRetryTimer=null)}clearIframeLoadTimer(){null!==this.iframeLoadTimer&&(window.clearTimeout(this.iframeLoadTimer),this.iframeLoadTimer=null)}mountOverlay(){const e=document.createElement("div");e.id="payinto-checkout-container",e.style.position="fixed",e.style.top="0",e.style.left="0",e.style.right="0",e.style.bottom="0",e.style.width="100%",e.style.minHeight="100vh",e.style.height="100%",e.style.backgroundColor="rgba(0, 0, 0, 0.85)",e.style.zIndex="9999",e.style.display="flex",e.style.alignItems="flex-start",e.style.justifyContent="center",e.style.overflowY="auto",e.style.paddingTop="6rem",e.style.paddingBottom="2rem",e.style.boxSizing="border-box";const t=document.createElement("div");t.className="payinto-preloader",t.style.display="flex",t.style.flexDirection="column",t.style.alignItems="center",t.style.justifyContent="center",t.style.position="absolute",t.style.top="6rem",t.style.left="50%",t.style.transform="translateX(-50%)",t.style.width="100%",t.style.maxWidth="720px",t.style.padding="4rem 0",t.style.zIndex="10",t.style.fontFamily="system-ui, -apple-system, sans-serif";const o=document.createElement("div");o.style.width="40px",o.style.height="40px",o.style.borderRadius="50%",o.style.border="2px solid transparent",o.style.borderBottom="2px solid #ffffff",o.style.marginBottom="16px",o.style.animation="spin 1s linear infinite";const i=document.createElement("p");i.id="payinto-checkout-subtitle",i.innerText="Initializing Secure Checkout...",i.style.fontSize="12px",i.style.color="#e5e7eb",i.style.margin="0",t.appendChild(o),t.appendChild(i),e.appendChild(t);const n=document.createElement("iframe");if(n.style.width="100%",n.style.maxWidth="720px",n.style.height="auto",n.style.border="none",n.style.backgroundColor="transparent",n.style.opacity="0",n.style.transition="opacity 0.25s ease",n.allow="payment; clipboard-read; clipboard-write",e.appendChild(n),!document.getElementById("payinto-checkout-styles")){const e=document.createElement("style");e.id="payinto-checkout-styles",e.innerHTML="\n @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }\n body.payinto-checkout-open { overflow: hidden !important; }\n @media (max-width: 767px) {\n #payinto-checkout-container {\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n right: 0 !important;\n bottom: 0 !important;\n height: 100% !important;\n min-height: 100vh !important;\n overflow-y: auto !important;\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .payinto-preloader { top: 2rem !important; }\n }\n ",document.head.appendChild(e)}document.body.appendChild(e),document.body.classList.add("payinto-checkout-open"),this.container=e,this.iframe=n}validateTokenMode(e){if(!e.token||"string"!=typeof e.token)throw new Error('Payinto Checkout: token is required for mode="token".');if(!e.checkout_url||"string"!=typeof e.checkout_url)throw new Error('Payinto Checkout: checkout_url is required for mode="token".');if(!new URL(e.checkout_url).origin)throw new Error("Payinto Checkout: invalid checkout_url.");return{token:e.token,checkout_url:e.checkout_url,merchant_reference:e.merchant_reference,checkout_version:e.checkout_version}}async initializePublicKeyMode(e){if(!e.public_key)throw new Error('Payinto Checkout: public_key is required for mode="public_key".');if(!e.customer?.email)throw new Error('Payinto Checkout: customer.email is required for mode="public_key".');if(!e.amount||e.amount<=0)throw new Error('Payinto Checkout: amount must be greater than zero for mode="public_key".');const t={merchant_reference:e.merchant_reference,amount:e.amount,currency:e.currency,customer:e.customer,redirect_url:e.redirect_url,metadata:e.metadata,settlement_account_id:e.settlement_account_id,checkout_version:e.checkout_version},o=new AbortController,i=window.setTimeout(()=>o.abort(),2e4);let n;try{n=await fetch("https://api.payinto.co/api/v1/checkout/initialize",{method:"POST",cache:"no-store",headers:{"Content-Type":"application/json",Accept:"application/json",Authorization:`Bearer ${e.public_key}`},body:JSON.stringify(t),signal:o.signal})}catch(c){if(c instanceof DOMException&&"AbortError"===c.name)throw new Error("Checkout initialization timed out. Please try again.");throw new Error("Unable to connect to checkout. Please check your connection and try again.")}finally{window.clearTimeout(i)}if(!n.ok){const e=(e=>{try{return JSON.parse(e)}catch{return null}})(await n.text());throw new Error(e?.message||"Failed to initialize transaction.")}const r=await n.json(),a=r.data||(r.token&&r.checkout_url?{token:r.token,checkout_url:r.checkout_url,merchant_reference:r.merchant_reference,checkout_version:r.checkout_version}:void 0);if(!a?.token||!a?.checkout_url)throw new Error("Payinto Checkout: invalid initialize response.");return{token:a.token,checkout_url:a.checkout_url,merchant_reference:a.merchant_reference,checkout_version:a.checkout_version}}initializeIframe(e){if(!this.iframe||!this.container)return;const t=this.container.querySelector("#payinto-checkout-subtitle"),o=this.container.querySelector(".payinto-preloader"),i=s(e),n=new URL(i).origin,r=new URL(e.checkout_url).origin,a=Array.from(new Set([n,r]));if(!c.has(n)&&n!==new URL(e.checkout_url).origin)throw new Error("Payinto Checkout: target origin is not in allowlist.");this.iframe.src=`${i}?updated=${Date.now()}`,this.iframeLoadTimer=window.setTimeout(()=>{this.iframeLoadTimer=null,this.handleError({type:"checkout.error",code:"iframe_load_timeout",message:"Checkout took too long to load. Please try again."})},2e4),this.iframe.onload=()=>{this.clearIframeLoadTimer(),o&&(o.style.display="none"),this.iframe&&(this.iframe.style.opacity="1");const t={type:"checkout.init",token:e.token,merchant_reference:e.merchant_reference,checkout_url:e.checkout_url};this.pendingInit={payload:t,origins:a};const i=()=>{if(this.pendingInit)for(const e of this.pendingInit.origins)this.iframe?.contentWindow?.postMessage(this.pendingInit.payload,e)};i(),this.clearInitRetry();let n=0;this.initRetryTimer=window.setInterval(()=>{n+=1,n>=8?this.clearInitRetry():i()},500)},this.iframe.onerror=()=>{this.clearIframeLoadTimer(),this.handleError({type:"checkout.error",code:"iframe_load_failed",message:"Unable to load checkout. Please check your connection and try again."})},t&&(t.innerText="Loading checkout...")}bindMessageListener(){const e=e=>{if(!c.has(e.origin))return;this.clearInitRetry();const t=e.data;if("object"==typeof t&&"checkout.resize"===t?.type&&this.iframe)this.iframe.style.height=`${Math.max(300,t.height||0)}px
|
|
1
|
+
var PayintoCheckoutBundle=function(e){"use strict";const t="undefined"!=typeof window,o=t?"https://checkout.payinto.co":"",i="/v1".replace(/\/+$/,""),n=`${o}${i}/popup`,r="https://checkout.payinto.co,https://go.payinto.co".split(",").map(e=>e.trim()).filter(Boolean),a=t?new URL(n).origin:"",c=new Set([a,...r]),s=e=>{try{const t=new URL(e.checkout_url),o=e.checkout_version||(e=>{try{const t=new URL(e).pathname.split("/").filter(Boolean)[0];return/^v[0-9]+$/i.test(t||"")?t:void 0}catch{return}})(e.checkout_url);return o?`${t.origin}/${o}/popup`:`${t.origin}/popup`}catch{return n}};const l=new class{container=null;iframe=null;messageListener=null;callbacks={};initRetryTimer=null;iframeLoadTimer=null;pendingInit=null;open(e){const t=(e=>{if("token"===e.mode)return e;if("public_key"===e.mode)return e;throw new Error('Payinto Checkout: invalid options. Provide a valid mode ("token" or "public_key").')})(e);this.container&&this.close(),this.callbacks={onClose:t.onClose,onSuccess:t.onSuccess,onError:t.onError,callback:t.callback},this.mountOverlay();(async()=>{try{const e="token"===t.mode?this.validateTokenMode(t):await this.initializePublicKeyMode(t);this.bindMessageListener(),this.initializeIframe(e)}catch(e){const t=e instanceof Error?e.message:"Unable to initialize checkout.";this.handleError({type:"checkout.error",code:"initialization_failed",message:t})}})()}pay(e){this.open(e)}close=()=>{this.clearInitRetry(),this.clearIframeLoadTimer(),this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null),this.container&&document.body.contains(this.container)&&document.body.removeChild(this.container),this.container=null,this.iframe=null,this.pendingInit=null,document.body.classList.remove("payinto-checkout-open")};clearInitRetry(){null!==this.initRetryTimer&&(window.clearInterval(this.initRetryTimer),this.initRetryTimer=null)}clearIframeLoadTimer(){null!==this.iframeLoadTimer&&(window.clearTimeout(this.iframeLoadTimer),this.iframeLoadTimer=null)}mountOverlay(){const e=document.createElement("div");e.id="payinto-checkout-container",e.style.position="fixed",e.style.top="0",e.style.left="0",e.style.right="0",e.style.bottom="0",e.style.width="100%",e.style.minHeight="100vh",e.style.height="100%",e.style.backgroundColor="rgba(0, 0, 0, 0.85)",e.style.zIndex="9999",e.style.display="flex",e.style.alignItems="flex-start",e.style.justifyContent="center",e.style.overflowY="auto",e.style.paddingTop="6rem",e.style.paddingBottom="2rem",e.style.boxSizing="border-box";const t=document.createElement("div");t.className="payinto-preloader",t.style.display="flex",t.style.flexDirection="column",t.style.alignItems="center",t.style.justifyContent="center",t.style.position="absolute",t.style.top="6rem",t.style.left="50%",t.style.transform="translateX(-50%)",t.style.width="100%",t.style.maxWidth="720px",t.style.padding="4rem 0",t.style.zIndex="10",t.style.fontFamily="system-ui, -apple-system, sans-serif";const o=document.createElement("div");o.style.width="40px",o.style.height="40px",o.style.borderRadius="50%",o.style.border="2px solid transparent",o.style.borderBottom="2px solid #ffffff",o.style.marginBottom="16px",o.style.animation="spin 1s linear infinite";const i=document.createElement("p");i.id="payinto-checkout-subtitle",i.innerText="Initializing Secure Checkout...",i.style.fontSize="12px",i.style.color="#e5e7eb",i.style.margin="0",t.appendChild(o),t.appendChild(i),e.appendChild(t);const n=document.createElement("iframe");if(n.style.width="100%",n.style.maxWidth="720px",n.style.height="auto",n.style.border="none",n.style.backgroundColor="transparent",n.style.opacity="0",n.style.transition="opacity 0.25s ease",n.allow="payment; clipboard-read; clipboard-write",e.appendChild(n),!document.getElementById("payinto-checkout-styles")){const e=document.createElement("style");e.id="payinto-checkout-styles",e.innerHTML="\n @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }\n body.payinto-checkout-open { overflow: hidden !important; }\n @media (max-width: 767px) {\n #payinto-checkout-container {\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n right: 0 !important;\n bottom: 0 !important;\n height: 100% !important;\n min-height: 100vh !important;\n overflow-y: auto !important;\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .payinto-preloader { top: 2rem !important; }\n }\n ",document.head.appendChild(e)}document.body.appendChild(e),document.body.classList.add("payinto-checkout-open"),this.container=e,this.iframe=n}validateTokenMode(e){if(!e.token||"string"!=typeof e.token)throw new Error('Payinto Checkout: token is required for mode="token".');if(!e.checkout_url||"string"!=typeof e.checkout_url)throw new Error('Payinto Checkout: checkout_url is required for mode="token".');if(!new URL(e.checkout_url).origin)throw new Error("Payinto Checkout: invalid checkout_url.");return{token:e.token,checkout_url:e.checkout_url,merchant_reference:e.merchant_reference,checkout_version:e.checkout_version}}async initializePublicKeyMode(e){if(!e.public_key)throw new Error('Payinto Checkout: public_key is required for mode="public_key".');if(!e.customer?.email)throw new Error('Payinto Checkout: customer.email is required for mode="public_key".');if(!e.amount||e.amount<=0)throw new Error('Payinto Checkout: amount must be greater than zero for mode="public_key".');const t={merchant_reference:e.merchant_reference,amount:e.amount,currency:e.currency,customer:e.customer,redirect_url:e.redirect_url,metadata:e.metadata,settlement_account_id:e.settlement_account_id,checkout_version:e.checkout_version},o=new AbortController,i=window.setTimeout(()=>o.abort(),2e4);let n;try{n=await fetch("https://api.payinto.co/api/v1/checkout/initialize",{method:"POST",cache:"no-store",headers:{"Content-Type":"application/json",Accept:"application/json",Authorization:`Bearer ${e.public_key}`},body:JSON.stringify(t),signal:o.signal})}catch(c){if(c instanceof DOMException&&"AbortError"===c.name)throw new Error("Checkout initialization timed out. Please try again.");throw new Error("Unable to connect to checkout. Please check your connection and try again.")}finally{window.clearTimeout(i)}if(!n.ok){const e=(e=>{try{return JSON.parse(e)}catch{return null}})(await n.text());throw new Error(e?.message||"Failed to initialize transaction.")}const r=await n.json(),a=r.data||(r.token&&r.checkout_url?{token:r.token,checkout_url:r.checkout_url,merchant_reference:r.merchant_reference,checkout_version:r.checkout_version}:void 0);if(!a?.token||!a?.checkout_url)throw new Error("Payinto Checkout: invalid initialize response.");return{token:a.token,checkout_url:a.checkout_url,merchant_reference:a.merchant_reference,checkout_version:a.checkout_version}}initializeIframe(e){if(!this.iframe||!this.container)return;const t=this.container.querySelector("#payinto-checkout-subtitle"),o=this.container.querySelector(".payinto-preloader"),i=s(e),n=new URL(i).origin,r=new URL(e.checkout_url).origin,a=Array.from(new Set([n,r]));if(!c.has(n)&&n!==new URL(e.checkout_url).origin)throw new Error("Payinto Checkout: target origin is not in allowlist.");this.iframe.src=`${i}?updated=${Date.now()}`,this.iframeLoadTimer=window.setTimeout(()=>{this.iframeLoadTimer=null,this.handleError({type:"checkout.error",code:"iframe_load_timeout",message:"Checkout took too long to load. Please try again."})},2e4),this.iframe.onload=()=>{this.clearIframeLoadTimer(),o&&(o.style.display="none"),this.iframe&&(this.iframe.style.opacity="1");const t={type:"checkout.init",token:e.token,merchant_reference:e.merchant_reference,checkout_url:e.checkout_url};this.pendingInit={payload:t,origins:a};const i=()=>{if(this.pendingInit)for(const e of this.pendingInit.origins)this.iframe?.contentWindow?.postMessage(this.pendingInit.payload,e)};i(),this.clearInitRetry();let n=0;this.initRetryTimer=window.setInterval(()=>{n+=1,n>=8?this.clearInitRetry():i()},500)},this.iframe.onerror=()=>{this.clearIframeLoadTimer(),this.handleError({type:"checkout.error",code:"iframe_load_failed",message:"Unable to load checkout. Please check your connection and try again."})},t&&(t.innerText="Loading checkout...")}bindMessageListener(){const e=e=>{if(!c.has(e.origin))return;this.clearInitRetry();const t=e.data;if("object"==typeof t&&"checkout.resize"===t?.type&&this.iframe)return void(this.iframe.style.height=`${Math.max(300,t.height||0)}px`);if("object"==typeof t&&"checkout.ready"===t?.type){if(this.pendingInit)for(const e of this.pendingInit.origins)this.iframe?.contentWindow?.postMessage(this.pendingInit.payload,e);return void this.clearInitRetry()}if("object"==typeof t&&"checkout.close"===t?.type)return this.callbacks.onClose?.(),void this.close();if("object"==typeof t?"checkout.succeeded"===t?.type||"checkout.success"===t?.type:"checkout.succeeded"===t||"checkout.success"===t){const e={..."object"==typeof t?t:{},type:"checkout.succeeded",status:"success"};return this.callbacks.onSuccess?.(e),this.callbacks.callback?.(e),void this.close()}"object"==typeof t&&"checkout.error"===t?.type&&this.handleError(t)};this.messageListener=e,window.addEventListener("message",e)}handleError(e){if(this.callbacks.onError?.(e),this.container){const t=this.container.querySelector(".payinto-preloader"),o=this.container.querySelector("#payinto-checkout-subtitle");t&&(t.style.display="flex"),o&&(o.innerText=e.message,o.style.color="#ff4444")}setTimeout(()=>{this.close()},1800)}},h={open:e=>l.open(e),pay:e=>l.pay(e),close:()=>l.close()},u=e=>{l.open(e)};return u.open=e=>l.open(e),u.pay=e=>l.pay(e),u.close=()=>l.close(),t&&(window.PayintoCheckout=u),e.PayintoCheckoutApi=h,e.default=h,Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}}),e}({});
|
|
@@ -1,10 +1,31 @@
|
|
|
1
|
+
/** Browser UI notification. Verify payment server-side before fulfillment. */
|
|
1
2
|
export type CheckoutSuccessPayload = {
|
|
2
|
-
type: 'checkout.
|
|
3
|
+
type: 'checkout.succeeded';
|
|
3
4
|
reference?: string;
|
|
4
5
|
merchant_reference?: string;
|
|
5
6
|
token?: string;
|
|
6
7
|
status: 'success';
|
|
7
8
|
};
|
|
9
|
+
/** Authenticated GET /api/v1/checkout/tsq/{reference} response data. */
|
|
10
|
+
export type CheckoutTransactionStatusData = {
|
|
11
|
+
reference: string;
|
|
12
|
+
merchant_reference: string | null;
|
|
13
|
+
provider_reference: string | null;
|
|
14
|
+
payment_status: 'pending' | 'successful' | 'failed' | 'cancelled' | 'expired';
|
|
15
|
+
currency: string;
|
|
16
|
+
base_amount: number;
|
|
17
|
+
customer_charge_amount: number;
|
|
18
|
+
customer_fee_amount: number;
|
|
19
|
+
amount: number;
|
|
20
|
+
charges_applied: boolean;
|
|
21
|
+
fee: number;
|
|
22
|
+
vat: number;
|
|
23
|
+
stamp_duty: number;
|
|
24
|
+
total_fee: number;
|
|
25
|
+
settled_amount: number;
|
|
26
|
+
paid_at: string | null;
|
|
27
|
+
transaction_id: string | null;
|
|
28
|
+
};
|
|
8
29
|
export type CheckoutErrorPayload = {
|
|
9
30
|
type: 'checkout.error';
|
|
10
31
|
code: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payinto/checkout-sdk",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.4",
|
|
4
|
+
"packageManager": "pnpm@12.3.4",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"description": "Payinto Checkout SDK for popup payments",
|
|
6
7
|
"main": "./dist/v1/checkout.es.js",
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
"author": "Payinto <hello@payinto.co>",
|
|
20
21
|
"license": "MIT",
|
|
21
22
|
"scripts": {
|
|
23
|
+
"pnpm:check-update": "node scripts/check-pnpm-version.mjs",
|
|
22
24
|
"dev": "vite",
|
|
23
25
|
"prepublishOnly": "npm run build:v1:sdk",
|
|
24
26
|
"build": "npm run build:v1:all",
|