@payinto/checkout-sdk 1.0.0

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 ADDED
@@ -0,0 +1,100 @@
1
+ # @payinto/checkout-sdk
2
+
3
+ The official Payinto Checkout SDK for web applications. Securely accept payments using the Payinto popup checkout.
4
+
5
+ ## Installation
6
+
7
+ ### npm
8
+ ```bash
9
+ npm install @payinto/checkout-sdk
10
+ ```
11
+
12
+ ### CDN
13
+ ```html
14
+ <script src="https://checkout.payinto.co/v1/checkout.js"></script>
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ### Modern Web App (npm)
20
+
21
+ ```ts
22
+ import PayintoCheckout from '@payinto/checkout-sdk';
23
+
24
+ const handlePayment = () => {
25
+ PayintoCheckout.open({
26
+ mode: 'public_key',
27
+ public_key: 'pk_live_your_public_key',
28
+ amount: 10000, // Amount in minor units (e.g., 10000 = 100.00)
29
+ currency: 'NGN',
30
+ customer: {
31
+ email: 'customer@example.com',
32
+ name: 'John Doe'
33
+ },
34
+ onSuccess: (response) => {
35
+ console.log('Payment successful:', response);
36
+ },
37
+ onClose: () => {
38
+ console.log('Checkout closed');
39
+ },
40
+ onError: (error) => {
41
+ console.error('Payment failed:', error);
42
+ }
43
+ });
44
+ };
45
+ ```
46
+
47
+ ### Script Tag (CDN)
48
+
49
+ ```javascript
50
+ window.PayintoCheckout.open({
51
+ mode: 'public_key',
52
+ public_key: 'pk_live_your_public_key',
53
+ // ...other options
54
+ });
55
+ ```
56
+
57
+ ## Options
58
+
59
+ | Option | Type | Required | Description |
60
+ |--------|------|----------|-------------|
61
+ | `mode` | `string` | Yes | Use `'public_key'` for merchant integrations. |
62
+ | `public_key` | `string` | Yes | Your Payinto public key. |
63
+ | `amount` | `number` | Yes | The transaction amount in minor units. |
64
+ | `currency` | `string` | No | Currency code (default: `'NGN'`). |
65
+ | `customer` | `object` | Yes | Customer details (`email` is required). |
66
+ | `reference` | `string` | No | Unique transaction reference. |
67
+ | `metadata` | `object` | No | Additional data for the transaction. |
68
+ | `redirect_url`| `string` | No | URL to redirect to after successful payment. |
69
+ | `onSuccess` | `function`| No | Callback on successful payment. |
70
+ | `onClose` | `function`| No | Callback when the checkout is closed. |
71
+ | `onError` | `function`| No | Callback on error. |
72
+
73
+ ## Event Payloads
74
+
75
+ ### Success
76
+ ```ts
77
+ {
78
+ type: 'checkout.success',
79
+ status: 'success',
80
+ reference: string,
81
+ token: string
82
+ }
83
+ ```
84
+
85
+ ### Error
86
+ ```ts
87
+ {
88
+ type: 'checkout.error',
89
+ code: string,
90
+ message: string
91
+ }
92
+ ```
93
+
94
+ ## Security
95
+ - The SDK validates message origins against an internal allowlist.
96
+ - It uses a secure iframe to isolate the payment process.
97
+ - **Never** expose your Secret Keys in client-side code.
98
+
99
+ ## License
100
+ MIT
@@ -0,0 +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:"",s=/* @__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 a=new class{container=null;iframe=null;messageListener=null;callbacks={};initRetryTimer=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.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,document.body.classList.remove("payinto-checkout-open")};clearInitRetry(){null!==this.initRetryTimer&&(window.clearInterval(this.initRetryTimer),this.initRetryTimer=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="650px",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 n=document.createElement("p");n.id="payinto-checkout-subtitle",n.innerText="Initializing Secure Checkout...",n.style.fontSize="12px",n.style.color="#e5e7eb",n.style.margin="0",t.appendChild(o),t.appendChild(n),e.appendChild(t);const r=document.createElement("iframe");if(r.src=i,r.style.width="100%",r.style.maxWidth="650px",r.style.height="auto",r.style.border="none",r.style.backgroundColor="transparent",r.style.opacity="0",r.style.transition="opacity 0.25s ease",r.allow="payment; clipboard-read; clipboard-write",e.appendChild(r),!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=r}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,reference:e.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={reference:e.reference,amount:e.amount,currency:e.currency,customer:e.customer,redirect_url:e.redirect_url,metadata:e.metadata,checkout_version:e.checkout_version},o=await fetch("https://api.payinto.co/api/v1/checkout/initialize",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json",Authorization:`Bearer ${e.public_key}`},body:JSON.stringify(t)});if(!o.ok){const e=(e=>{try{return JSON.parse(e)}catch{return null}})(await o.text());throw new Error(e?.message||"Failed to initialize transaction.")}const i=await o.json(),n=i.data||(i.token&&i.checkout_url?{token:i.token,checkout_url:i.checkout_url,reference:i.reference,checkout_version:i.checkout_version}:void 0);if(!n?.token||!n?.checkout_url)throw new Error("Payinto Checkout: invalid initialize response.");return{token:n.token,checkout_url:n.checkout_url,reference:n.reference,checkout_version:n.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,a=Array.from(/* @__PURE__ */new Set([n,r]));if(!s.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.iframe.onload=()=>{o&&(o.style.display="none"),this.iframe&&(this.iframe.style.opacity="1");const t=()=>{const t={type:"checkout.init",token:e.token,reference:e.reference,checkout_url:e.checkout_url};for(const e of a)this.iframe?.contentWindow?.postMessage(t,e)};t(),this.clearInitRetry();let i=0;this.initRetryTimer=window.setInterval(()=>{i+=1,i>=8?this.clearInitRetry():t()},500)},t&&(t.innerText="Loading checkout...")}bindMessageListener(){const e=e=>{if(!s.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`;else{if("object"==typeof t&&"checkout.close"===t?.type)return this.callbacks.onClose?.(),void this.close();if("object"==typeof t&&"checkout.success"===t?.type||"checkout.success"===t){const e="object"==typeof t?t:{type:"checkout.success",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=>a.open(e),pay:e=>a.pay(e),close:()=>a.close()},u=e=>{a.open(e)};u.open=e=>a.open(e),u.pay=e=>a.pay(e),u.close=()=>a.close(),e&&(window.PayintoCheckout=u);export{l as PayintoCheckoutApi,l as default};
@@ -0,0 +1 @@
1
+ var PayintoCheckout=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),s=t?new URL(n).origin:"",c=new Set([s,...r]),a=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;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.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,document.body.classList.remove("payinto-checkout-open")};clearInitRetry(){null!==this.initRetryTimer&&(window.clearInterval(this.initRetryTimer),this.initRetryTimer=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="650px",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 r=document.createElement("iframe");if(r.src=n,r.style.width="100%",r.style.maxWidth="650px",r.style.height="auto",r.style.border="none",r.style.backgroundColor="transparent",r.style.opacity="0",r.style.transition="opacity 0.25s ease",r.allow="payment; clipboard-read; clipboard-write",e.appendChild(r),!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=r}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,reference:e.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={reference:e.reference,amount:e.amount,currency:e.currency,customer:e.customer,redirect_url:e.redirect_url,metadata:e.metadata,checkout_version:e.checkout_version},o=await fetch("https://api.payinto.co/api/v1/checkout/initialize",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json",Authorization:`Bearer ${e.public_key}`},body:JSON.stringify(t)});if(!o.ok){const e=(e=>{try{return JSON.parse(e)}catch{return null}})(await o.text());throw new Error(e?.message||"Failed to initialize transaction.")}const i=await o.json(),n=i.data||(i.token&&i.checkout_url?{token:i.token,checkout_url:i.checkout_url,reference:i.reference,checkout_version:i.checkout_version}:void 0);if(!n?.token||!n?.checkout_url)throw new Error("Payinto Checkout: invalid initialize response.");return{token:n.token,checkout_url:n.checkout_url,reference:n.reference,checkout_version:n.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=a(e),n=new URL(i).origin,r=new URL(e.checkout_url).origin,s=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.iframe.onload=()=>{o&&(o.style.display="none"),this.iframe&&(this.iframe.style.opacity="1");const t=()=>{const t={type:"checkout.init",token:e.token,reference:e.reference,checkout_url:e.checkout_url};for(const e of s)this.iframe?.contentWindow?.postMessage(t,e)};t(),this.clearInitRetry();let i=0;this.initRetryTimer=window.setInterval(()=>{i+=1,i>=8?this.clearInitRetry():t()},500)},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`;else{if("object"==typeof t&&"checkout.close"===t?.type)return this.callbacks.onClose?.(),void this.close();if("object"==typeof t&&"checkout.success"===t?.type||"checkout.success"===t){const e="object"==typeof t?t:{type:"checkout.success",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)}},u={open:e=>l.open(e),pay:e=>l.pay(e),close:()=>l.close()},h=e=>{l.open(e)};return h.open=e=>l.open(e),h.pay=e=>l.pay(e),h.close=()=>l.close(),t&&(window.PayintoCheckout=h),e.PayintoCheckoutApi=u,e.default=u,Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}}),e}({});
Binary file
@@ -0,0 +1,180 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Payinto Checkout Demo</title>
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
17
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
18
+ min-height: 100vh;
19
+ display: flex;
20
+ align-items: center;
21
+ justify-content: center;
22
+ padding: 20px;
23
+ }
24
+
25
+ .demo-container {
26
+ background: white;
27
+ border-radius: 16px;
28
+ padding: 40px;
29
+ max-width: 500px;
30
+ width: 100%;
31
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 28px;
36
+ font-weight: 700;
37
+ margin-bottom: 8px;
38
+ color: #1a202c;
39
+ }
40
+
41
+ p {
42
+ color: #718096;
43
+ margin-bottom: 32px;
44
+ font-size: 14px;
45
+ }
46
+
47
+ .button-group {
48
+ display: flex;
49
+ flex-direction: column;
50
+ gap: 12px;
51
+ }
52
+
53
+ button {
54
+ background: #1a202c;
55
+ color: white;
56
+ border: none;
57
+ padding: 14px 24px;
58
+ border-radius: 8px;
59
+ font-size: 14px;
60
+ font-weight: 600;
61
+ cursor: pointer;
62
+ transition: all 0.2s;
63
+ }
64
+
65
+ button:hover {
66
+ background: #2d3748;
67
+ transform: translateY(-2px);
68
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
69
+ }
70
+
71
+ button:active {
72
+ transform: translateY(0);
73
+ }
74
+
75
+ .code-block {
76
+ background: #f7fafc;
77
+ border: 1px solid #e2e8f0;
78
+ border-radius: 8px;
79
+ padding: 16px;
80
+ margin-top: 32px;
81
+ font-family: 'Monaco', 'Courier New', monospace;
82
+ font-size: 12px;
83
+ color: #2d3748;
84
+ overflow-x: auto;
85
+ }
86
+
87
+ .code-block pre {
88
+ margin: 0;
89
+ white-space: pre-wrap;
90
+ word-wrap: break-word;
91
+ }
92
+
93
+ .badge {
94
+ display: inline-block;
95
+ background: #48bb78;
96
+ color: white;
97
+ padding: 4px 8px;
98
+ border-radius: 4px;
99
+ font-size: 10px;
100
+ font-weight: 700;
101
+ text-transform: uppercase;
102
+ margin-bottom: 16px;
103
+ }
104
+ </style>
105
+ </head>
106
+
107
+ <body>
108
+ <div class="demo-container">
109
+ <span class="badge">Demo</span>
110
+ <h1>Payinto Checkout</h1>
111
+ <p>Click the button below to test the payment checkout integration.</p>
112
+
113
+ <div class="button-group">
114
+ <button onclick="openCheckout()">
115
+ Pay NGN 10,000
116
+ </button>
117
+ </div>
118
+
119
+ <div class="code-block">
120
+ <pre>PayintoCheckout({
121
+ mode: 'public_key',
122
+ public_key: 'pk_test_123456789',
123
+ reference: 'TRANS_123456',
124
+ amount: 100000,
125
+ currency: 'NGN',
126
+ customer: {
127
+ email: 'customer@example.com',
128
+ name: 'John Doe',
129
+ phone_number: '08123456789'
130
+ },
131
+ metadata: {
132
+ custom_field: 'custom_value'
133
+ },
134
+ // With Redirect URL to redirect to after payment
135
+ redirect_url: 'https://example.com/redirect', // URL
136
+ // OR Alternatively, you can specify a callback function to handle payment response
137
+ onClose: function () {
138
+ console.log('Checkout closed');
139
+ },
140
+ callback: function (response) {
141
+ console.log('Payment successful:', response);
142
+ }
143
+
144
+ });</pre>
145
+ </div>
146
+ </div>
147
+
148
+ <!-- Payinto Checkout SDK -->
149
+ <script src="/checkout.js"></script>
150
+
151
+ <script>
152
+ function openCheckout() {
153
+ PayintoCheckout({
154
+ mode: 'public_key',
155
+ public_key: 'pk_live_784683f25db04c05838ee730ce489e27e46e', // Example public key
156
+ reference: 'TRANS_123456',
157
+ amount: 100000,
158
+ currency: 'NGN',
159
+ customer: {
160
+ name: 'John Doe',
161
+ email: 'john.doe@example.com',
162
+ phone_number: '08123456789'
163
+ },
164
+ redirect_url: 'https://example.com/redirect',
165
+ metadata: {
166
+ custom_field: 'custom_value'
167
+ },
168
+ onClose: function () {
169
+ console.log('Checkout closed');
170
+ },
171
+ callback: function (response) {
172
+ console.log('Payment successful:', response);
173
+ // alert('Payment successful! ' + JSON.stringify(response));
174
+ }
175
+ });
176
+ }
177
+ </script>
178
+ </body>
179
+
180
+ </html>
@@ -0,0 +1,43 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <title>Payinto Checkout Demo</title>
6
+ </head>
7
+
8
+ <body>
9
+ <h1>Checkout Demo</h1>
10
+ <button onclick="makePayment()">Click Me!</button>
11
+
12
+ <script src="https://checkout.payinto.co/v1/checkout.js"></script>
13
+ <script type="text/javascript">
14
+ function makePayment() {
15
+ PayintoCheckout({
16
+ mode: 'public_key',
17
+ public_key: 'pk_live_xxxxxxxxxxxxxxxxxxx', // Example public key
18
+ reference: '12345678-1234-1234-1234-123456789012',
19
+ amount: 100000, // Amount in the smallest currency unit (e.g. kobo, cents)
20
+ currency: 'NGN',
21
+ customer: {
22
+ name: 'John Doe',
23
+ email: 'john.doe@example.com',
24
+ phone_number: '08123456789'
25
+ },
26
+ metadata: {
27
+ custom_field: 'custom_value'
28
+ },
29
+ // With Redirect URL to redirect to after payment
30
+ redirect_url: 'https://example.com/redirect', // URL
31
+ // OR Alternatively, you can specify a callback function to handle payment response
32
+ onClose: function () {
33
+ console.log('Checkout closed');
34
+ },
35
+ callback: function (response) {
36
+ console.log('Payment successful:', response);
37
+ }
38
+ });
39
+ }
40
+ </script>
41
+ </body>
42
+
43
+ </html>
Binary file
@@ -0,0 +1,56 @@
1
+ export type CheckoutSuccessPayload = {
2
+ type: 'checkout.success';
3
+ reference?: string;
4
+ token?: string;
5
+ status: 'success';
6
+ };
7
+ export type CheckoutErrorPayload = {
8
+ type: 'checkout.error';
9
+ code: string;
10
+ message: string;
11
+ };
12
+ type CheckoutCallbacks = {
13
+ onClose?: () => void;
14
+ onSuccess?: (payload: CheckoutSuccessPayload) => void;
15
+ onError?: (payload: CheckoutErrorPayload) => void;
16
+ callback?: (payload: string | CheckoutSuccessPayload) => void;
17
+ };
18
+ type TokenModeOptions = CheckoutCallbacks & {
19
+ mode: 'token';
20
+ token: string;
21
+ checkout_url: string;
22
+ reference?: string;
23
+ checkout_version?: string;
24
+ };
25
+ type PublicKeyModeOptions = CheckoutCallbacks & {
26
+ mode: 'public_key';
27
+ public_key: string;
28
+ reference?: string;
29
+ amount: number;
30
+ currency?: string;
31
+ customer: {
32
+ email: string;
33
+ name?: string;
34
+ phone_number?: string;
35
+ };
36
+ redirect_url?: string;
37
+ metadata?: Record<string, unknown>;
38
+ checkout_version?: string;
39
+ };
40
+ export type CheckoutOpenOptions = TokenModeOptions | PublicKeyModeOptions;
41
+ type CheckoutGlobalCallable = ((options: CheckoutOpenOptions) => void) & {
42
+ open: (options: CheckoutOpenOptions) => void;
43
+ pay: (options: CheckoutOpenOptions) => void;
44
+ close: () => void;
45
+ };
46
+ export declare const PayintoCheckoutApi: {
47
+ open: (options: CheckoutOpenOptions) => void;
48
+ pay: (options: CheckoutOpenOptions) => void;
49
+ close: () => void;
50
+ };
51
+ declare global {
52
+ interface Window {
53
+ PayintoCheckout: CheckoutGlobalCallable;
54
+ }
55
+ }
56
+ export default PayintoCheckoutApi;
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@payinto/checkout-sdk",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "Payinto Checkout SDK for popup payments (token and public_key modes)",
6
+ "main": "./dist/v1/checkout.es.js",
7
+ "module": "./dist/v1/checkout.es.js",
8
+ "types": "./dist/v1/types/checkout-sdk.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/v1/checkout.es.js",
12
+ "types": "./dist/v1/types/checkout-sdk.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist/v1",
17
+ "README.md"
18
+ ],
19
+ "author": "Payinto <hello@payinto.co>",
20
+ "license": "MIT",
21
+ "scripts": {
22
+ "dev": "vite",
23
+ "prepublishOnly": "npm run build:v1:sdk",
24
+ "build": "npm run build:v1:all",
25
+ "build:v1:sdk": "vite build --config vite.v1.sdk.config.ts && npm run build:v1:sdk:types && npm run copy:sdk",
26
+ "build:v1:sdk:types": "tsc -p tsconfig.sdk.json",
27
+ "build:v1:app": "tsc -b && vite build --config vite.v1.app.config.ts",
28
+ "build:v1:all": "npm run build:v1:sdk && npm run build:v1:app",
29
+ "build:sdk": "npm run build:v1:sdk",
30
+ "build:app": "npm run build:v1:app",
31
+ "build:all": "npm run build:v1:all",
32
+ "copy:sdk": "cp dist/v1/checkout.js public/checkout.js",
33
+ "lint": "eslint .",
34
+ "preview": "vite preview"
35
+ },
36
+ "keywords": [
37
+ "payinto",
38
+ "checkout",
39
+ "payments",
40
+ "sdk"
41
+ ],
42
+ "dependencies": {},
43
+ "devDependencies": {
44
+ "react": "^19.2.0",
45
+ "react-dom": "^19.2.0",
46
+ "react-router-dom": "^7.10.1",
47
+ "@eslint/js": "^9.39.1",
48
+ "@types/node": "^24.10.1",
49
+ "@types/react": "^19.2.5",
50
+ "@types/react-dom": "^19.2.3",
51
+ "@vitejs/plugin-react": "^5.1.1",
52
+ "autoprefixer": "^10.4.22",
53
+ "eslint": "^9.39.1",
54
+ "eslint-plugin-react-hooks": "^7.0.1",
55
+ "eslint-plugin-react-refresh": "^0.4.24",
56
+ "globals": "^16.5.0",
57
+ "postcss": "^8.5.6",
58
+ "tailwindcss": "^3.4.19",
59
+ "terser": "^5.44.1",
60
+ "typescript": "~5.9.3",
61
+ "typescript-eslint": "^8.46.4",
62
+ "vite": "^7.2.4"
63
+ }
64
+ }