@loofta/pay-sdk 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -0
- package/dist/index.d.mts +43 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +206 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +202 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# @loofta/pay-sdk
|
|
2
|
+
|
|
3
|
+
Embeddable **Loofta Pay Button** for React. The button opens **Loofta’s hosted checkout** — your customers pay there, and funds go to the destination wallet, network, and token configured in your organization.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- **Organization ID** — You must have an organization set up with Loofta. Your **destination wallet address**, **network**, and **token** are configured in that organization.
|
|
8
|
+
- **Don’t have an organization ID?** [Contact Loofta](https://t.me/looftaxyz) to get one.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @loofta/pay-sdk
|
|
14
|
+
# or
|
|
15
|
+
yarn add @loofta/pay-sdk
|
|
16
|
+
pnpm add @loofta/pay-sdk
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### React
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { PayButton } from '@loofta/pay-sdk';
|
|
25
|
+
|
|
26
|
+
<PayButton
|
|
27
|
+
organizationId="your-org-id"
|
|
28
|
+
amount={100}
|
|
29
|
+
buttonText="Pay $100"
|
|
30
|
+
onSuccess={(paymentId) => {
|
|
31
|
+
console.log('Payment completed:', paymentId);
|
|
32
|
+
}}
|
|
33
|
+
/>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
You usually **don’t** need `checkoutBaseUrl`. The button opens Loofta’s checkout; if your app is on a different domain, the default URL is used. Only set `checkoutBaseUrl` if Loofta has given you a specific checkout URL.
|
|
37
|
+
|
|
38
|
+
### Props
|
|
39
|
+
|
|
40
|
+
| Prop | Type | Description |
|
|
41
|
+
|------|------|-------------|
|
|
42
|
+
| `organizationId` | `string` | **Required.** Your Loofta organization ID. Destination wallet, network, and token are set in your organization. Don’t have one? Contact Loofta. |
|
|
43
|
+
| `amount` | `number \| string` | Optional payment amount (USD). |
|
|
44
|
+
| `checkoutBaseUrl` | `string` | Optional. URL of the Loofta Pay checkout page. Only set if the button is on a different domain and you need to point to a specific Loofta checkout URL; otherwise omit. |
|
|
45
|
+
| `buttonBgColor` | `string` | Optional button background (CSS color). |
|
|
46
|
+
| `pageBgColor` | `string` | Optional checkout page background. |
|
|
47
|
+
| `callbackUrl` | `string` | Optional URL to redirect after payment. |
|
|
48
|
+
| `onSuccess` | `(paymentId: string) => void` | Called when payment completes (popup mode). |
|
|
49
|
+
| `buttonText` | `string` | Button label (default: "Pay with Loofta"). |
|
|
50
|
+
| `successText` | `string` | Text after success (default: "Paid Successfully"). |
|
|
51
|
+
| `openMode` | `'popup' \| 'redirect' \| 'tab'` | How to open checkout (default: `popup`). |
|
|
52
|
+
| `disabled` | `boolean` | Disable the button. |
|
|
53
|
+
| `className` | `string` | Optional CSS class. |
|
|
54
|
+
|
|
55
|
+
### How it works
|
|
56
|
+
|
|
57
|
+
1. User clicks the button on your site.
|
|
58
|
+
2. Loofta’s hosted checkout opens (popup, new tab, or redirect).
|
|
59
|
+
3. User pays with the token/network they choose; funds are sent to **your organization’s configured destination wallet, network, and token**.
|
|
60
|
+
4. You get the outcome via `onSuccess` and/or `callbackUrl`.
|
|
61
|
+
|
|
62
|
+
### Embed code helpers
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
import { generateEmbedCode, generateScriptEmbed } from '@loofta/pay-sdk';
|
|
66
|
+
|
|
67
|
+
const reactSnippet = generateEmbedCode({
|
|
68
|
+
organizationId: 'your-org-id',
|
|
69
|
+
amount: 100,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const scriptSnippet = generateScriptEmbed({
|
|
73
|
+
organizationId: 'your-org-id',
|
|
74
|
+
amount: 100,
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Build
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
cd packages/loofta-pay-sdk
|
|
82
|
+
npm install
|
|
83
|
+
npm run build
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Output: `dist/` (ESM, CJS, and types).
|
|
87
|
+
|
|
88
|
+
## Publish to npm
|
|
89
|
+
|
|
90
|
+
1. **Build the package**
|
|
91
|
+
```bash
|
|
92
|
+
cd packages/loofta-pay-sdk
|
|
93
|
+
npm install
|
|
94
|
+
npm run build
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
2. **Log in to npm** (create an account at [npmjs.com](https://www.npmjs.com) if needed)
|
|
98
|
+
```bash
|
|
99
|
+
npm login
|
|
100
|
+
```
|
|
101
|
+
Enter your npm username, password, and email when prompted.
|
|
102
|
+
|
|
103
|
+
3. **Publish**
|
|
104
|
+
```bash
|
|
105
|
+
npm publish
|
|
106
|
+
```
|
|
107
|
+
Scoped packages like `@loofta/pay-sdk` use `publishConfig.access: "public"` in package.json, so you don’t need `--access public` each time.
|
|
108
|
+
|
|
109
|
+
4. **Later releases**: bump `version` in `package.json`, run `npm run build`, then `npm publish` again.
|
|
110
|
+
|
|
111
|
+
## License
|
|
112
|
+
|
|
113
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
interface PayButtonProps {
|
|
4
|
+
/**
|
|
5
|
+
* Organization ID (required). Must be set up with Loofta — your destination wallet, network, and token are configured in your organization.
|
|
6
|
+
* If you don't have an organization ID, contact Loofta to get one.
|
|
7
|
+
*/
|
|
8
|
+
organizationId: string;
|
|
9
|
+
/** Payment amount in USD (optional) */
|
|
10
|
+
amount?: number | string;
|
|
11
|
+
/** Button background color (optional) */
|
|
12
|
+
buttonBgColor?: string;
|
|
13
|
+
/** Checkout page background color (optional - falls back to org settings) */
|
|
14
|
+
pageBgColor?: string;
|
|
15
|
+
/** @deprecated Use buttonBgColor instead */
|
|
16
|
+
bgColor?: string;
|
|
17
|
+
/** Callback URL after payment (optional) */
|
|
18
|
+
callbackUrl?: string;
|
|
19
|
+
/** Callback function after payment (optional) */
|
|
20
|
+
onSuccess?: (paymentId: string) => void;
|
|
21
|
+
/** Button text (optional) */
|
|
22
|
+
buttonText?: string;
|
|
23
|
+
/** Success text shown after payment (optional) */
|
|
24
|
+
successText?: string;
|
|
25
|
+
/** Custom className for button styling */
|
|
26
|
+
className?: string;
|
|
27
|
+
/** Open checkout in new tab vs popup (default: popup) */
|
|
28
|
+
openMode?: 'popup' | 'redirect' | 'tab';
|
|
29
|
+
/** Disable button */
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Base URL of the Loofta Pay hosted checkout page. Checkout is always Loofta's — this is only needed if the button is embedded on a different domain than the default Loofta Pay app. Usually omit; default is used.
|
|
33
|
+
*/
|
|
34
|
+
checkoutBaseUrl?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Loofta Pay Button – opens your Loofta Pay checkout (same API as your app).
|
|
38
|
+
*/
|
|
39
|
+
declare function PayButton({ organizationId, amount, buttonBgColor, pageBgColor, bgColor, callbackUrl, onSuccess, buttonText, successText, className, openMode, disabled, checkoutBaseUrl, }: PayButtonProps): react_jsx_runtime.JSX.Element;
|
|
40
|
+
declare function generateEmbedCode(props: Partial<PayButtonProps>): string;
|
|
41
|
+
declare function generateScriptEmbed(props: Partial<PayButtonProps>): string;
|
|
42
|
+
|
|
43
|
+
export { PayButton, type PayButtonProps, generateEmbedCode, generateScriptEmbed };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
interface PayButtonProps {
|
|
4
|
+
/**
|
|
5
|
+
* Organization ID (required). Must be set up with Loofta — your destination wallet, network, and token are configured in your organization.
|
|
6
|
+
* If you don't have an organization ID, contact Loofta to get one.
|
|
7
|
+
*/
|
|
8
|
+
organizationId: string;
|
|
9
|
+
/** Payment amount in USD (optional) */
|
|
10
|
+
amount?: number | string;
|
|
11
|
+
/** Button background color (optional) */
|
|
12
|
+
buttonBgColor?: string;
|
|
13
|
+
/** Checkout page background color (optional - falls back to org settings) */
|
|
14
|
+
pageBgColor?: string;
|
|
15
|
+
/** @deprecated Use buttonBgColor instead */
|
|
16
|
+
bgColor?: string;
|
|
17
|
+
/** Callback URL after payment (optional) */
|
|
18
|
+
callbackUrl?: string;
|
|
19
|
+
/** Callback function after payment (optional) */
|
|
20
|
+
onSuccess?: (paymentId: string) => void;
|
|
21
|
+
/** Button text (optional) */
|
|
22
|
+
buttonText?: string;
|
|
23
|
+
/** Success text shown after payment (optional) */
|
|
24
|
+
successText?: string;
|
|
25
|
+
/** Custom className for button styling */
|
|
26
|
+
className?: string;
|
|
27
|
+
/** Open checkout in new tab vs popup (default: popup) */
|
|
28
|
+
openMode?: 'popup' | 'redirect' | 'tab';
|
|
29
|
+
/** Disable button */
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Base URL of the Loofta Pay hosted checkout page. Checkout is always Loofta's — this is only needed if the button is embedded on a different domain than the default Loofta Pay app. Usually omit; default is used.
|
|
33
|
+
*/
|
|
34
|
+
checkoutBaseUrl?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Loofta Pay Button – opens your Loofta Pay checkout (same API as your app).
|
|
38
|
+
*/
|
|
39
|
+
declare function PayButton({ organizationId, amount, buttonBgColor, pageBgColor, bgColor, callbackUrl, onSuccess, buttonText, successText, className, openMode, disabled, checkoutBaseUrl, }: PayButtonProps): react_jsx_runtime.JSX.Element;
|
|
40
|
+
declare function generateEmbedCode(props: Partial<PayButtonProps>): string;
|
|
41
|
+
declare function generateScriptEmbed(props: Partial<PayButtonProps>): string;
|
|
42
|
+
|
|
43
|
+
export { PayButton, type PayButtonProps, generateEmbedCode, generateScriptEmbed };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
|
|
6
|
+
// src/PayButton.tsx
|
|
7
|
+
var DEFAULT_STYLE = {
|
|
8
|
+
background: "linear-gradient(to right, #FF0F00, #EAB308)",
|
|
9
|
+
color: "#ffffff",
|
|
10
|
+
border: "none",
|
|
11
|
+
borderRadius: "12px",
|
|
12
|
+
padding: "12px 24px",
|
|
13
|
+
fontSize: "16px",
|
|
14
|
+
fontWeight: 600,
|
|
15
|
+
cursor: "pointer",
|
|
16
|
+
display: "inline-flex",
|
|
17
|
+
alignItems: "center",
|
|
18
|
+
justifyContent: "center",
|
|
19
|
+
gap: "8px",
|
|
20
|
+
minWidth: "160px",
|
|
21
|
+
transition: "transform 0.2s, box-shadow 0.2s",
|
|
22
|
+
boxShadow: "0 4px 14px rgba(255, 15, 0, 0.25)"
|
|
23
|
+
};
|
|
24
|
+
var HOVER_STYLE = {
|
|
25
|
+
transform: "scale(1.02)",
|
|
26
|
+
boxShadow: "0 6px 20px rgba(255, 15, 0, 0.35)"
|
|
27
|
+
};
|
|
28
|
+
function LoaderIcon() {
|
|
29
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
|
|
30
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10", strokeOpacity: 0.25 }),
|
|
31
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
32
|
+
"path",
|
|
33
|
+
{
|
|
34
|
+
d: "M12 2a10 10 0 0 1 10 10",
|
|
35
|
+
style: { transformOrigin: "12px 12px" },
|
|
36
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
37
|
+
"animateTransform",
|
|
38
|
+
{
|
|
39
|
+
attributeName: "transform",
|
|
40
|
+
type: "rotate",
|
|
41
|
+
from: "0 12 12",
|
|
42
|
+
to: "360 12 12",
|
|
43
|
+
dur: "0.8s",
|
|
44
|
+
repeatCount: "indefinite"
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
] });
|
|
50
|
+
}
|
|
51
|
+
function CheckIcon() {
|
|
52
|
+
return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "20 6 9 17 4 12" }) });
|
|
53
|
+
}
|
|
54
|
+
function PayIcon() {
|
|
55
|
+
return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" }) });
|
|
56
|
+
}
|
|
57
|
+
function PayButton({
|
|
58
|
+
organizationId,
|
|
59
|
+
amount,
|
|
60
|
+
buttonBgColor,
|
|
61
|
+
pageBgColor,
|
|
62
|
+
bgColor,
|
|
63
|
+
callbackUrl,
|
|
64
|
+
onSuccess,
|
|
65
|
+
buttonText = "Pay with Loofta",
|
|
66
|
+
successText = "Paid Successfully",
|
|
67
|
+
className,
|
|
68
|
+
openMode = "popup",
|
|
69
|
+
disabled = false,
|
|
70
|
+
checkoutBaseUrl
|
|
71
|
+
}) {
|
|
72
|
+
const [loading, setLoading] = react.useState(false);
|
|
73
|
+
const [paid, setPaid] = react.useState(false);
|
|
74
|
+
const [isHovered, setIsHovered] = react.useState(false);
|
|
75
|
+
const effectiveButtonBgColor = buttonBgColor || bgColor;
|
|
76
|
+
const getCheckoutUrl = react.useCallback(() => {
|
|
77
|
+
const defaultBase = "https://pay.loofta.com";
|
|
78
|
+
const base = checkoutBaseUrl != null && checkoutBaseUrl !== "" ? checkoutBaseUrl.replace(/\/$/, "") : defaultBase;
|
|
79
|
+
const path = base.endsWith("/checkout") ? base : `${base}/checkout`;
|
|
80
|
+
const params = new URLSearchParams();
|
|
81
|
+
params.set("organizationId", organizationId);
|
|
82
|
+
if (amount) params.set("amount", String(amount));
|
|
83
|
+
if (pageBgColor) params.set("bgColor", encodeURIComponent(pageBgColor));
|
|
84
|
+
if (callbackUrl) params.set("callback", encodeURIComponent(callbackUrl));
|
|
85
|
+
return `${path}?${params.toString()}`;
|
|
86
|
+
}, [checkoutBaseUrl, organizationId, amount, pageBgColor, callbackUrl]);
|
|
87
|
+
const handleClick = react.useCallback(() => {
|
|
88
|
+
if (disabled || loading || paid) return;
|
|
89
|
+
setLoading(true);
|
|
90
|
+
const url = getCheckoutUrl();
|
|
91
|
+
if (openMode === "redirect") {
|
|
92
|
+
window.location.href = url;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (openMode === "tab") {
|
|
96
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
97
|
+
setLoading(false);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const width = 500;
|
|
101
|
+
const height = 700;
|
|
102
|
+
const left = window.screenX + (window.outerWidth - width) / 2;
|
|
103
|
+
const top = window.screenY + (window.outerHeight - height) / 2;
|
|
104
|
+
const popup = window.open(
|
|
105
|
+
url,
|
|
106
|
+
"loofta-pay-checkout",
|
|
107
|
+
`width=${width},height=${height},left=${left},top=${top},scrollbars=yes,resizable=yes`
|
|
108
|
+
);
|
|
109
|
+
const handleMessage = (event) => {
|
|
110
|
+
if (event.data?.type === "loofta-payment-success") {
|
|
111
|
+
setLoading(false);
|
|
112
|
+
setPaid(true);
|
|
113
|
+
onSuccess?.(event.data.paymentId);
|
|
114
|
+
popup?.close();
|
|
115
|
+
window.removeEventListener("message", handleMessage);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
window.addEventListener("message", handleMessage);
|
|
119
|
+
const checkClosed = setInterval(() => {
|
|
120
|
+
if (popup?.closed) {
|
|
121
|
+
clearInterval(checkClosed);
|
|
122
|
+
setLoading(false);
|
|
123
|
+
window.removeEventListener("message", handleMessage);
|
|
124
|
+
}
|
|
125
|
+
}, 500);
|
|
126
|
+
}, [disabled, loading, paid, getCheckoutUrl, openMode, onSuccess]);
|
|
127
|
+
const successStyle = {
|
|
128
|
+
...DEFAULT_STYLE,
|
|
129
|
+
background: "linear-gradient(to right, #10B981, #059669)",
|
|
130
|
+
boxShadow: "0 4px 14px rgba(16, 185, 129, 0.25)",
|
|
131
|
+
cursor: "default"
|
|
132
|
+
};
|
|
133
|
+
const buttonStyle = {
|
|
134
|
+
...DEFAULT_STYLE,
|
|
135
|
+
...effectiveButtonBgColor ? { background: effectiveButtonBgColor } : {},
|
|
136
|
+
...isHovered && !disabled && !paid ? HOVER_STYLE : {},
|
|
137
|
+
...disabled ? { opacity: 0.6, cursor: "not-allowed" } : {}
|
|
138
|
+
};
|
|
139
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
140
|
+
"button",
|
|
141
|
+
{
|
|
142
|
+
type: "button",
|
|
143
|
+
onClick: handleClick,
|
|
144
|
+
disabled: disabled || loading || paid,
|
|
145
|
+
className,
|
|
146
|
+
style: className ? void 0 : paid ? successStyle : buttonStyle,
|
|
147
|
+
onMouseEnter: () => setIsHovered(true),
|
|
148
|
+
onMouseLeave: () => setIsHovered(false),
|
|
149
|
+
children: paid ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
150
|
+
/* @__PURE__ */ jsxRuntime.jsx(CheckIcon, {}),
|
|
151
|
+
successText
|
|
152
|
+
] }) : loading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
153
|
+
/* @__PURE__ */ jsxRuntime.jsx(LoaderIcon, {}),
|
|
154
|
+
"Processing..."
|
|
155
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
156
|
+
/* @__PURE__ */ jsxRuntime.jsx(PayIcon, {}),
|
|
157
|
+
buttonText
|
|
158
|
+
] })
|
|
159
|
+
}
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
function generateEmbedCode(props) {
|
|
163
|
+
const { organizationId, amount, buttonBgColor, pageBgColor, callbackUrl, buttonText, checkoutBaseUrl } = props;
|
|
164
|
+
const propsStr = [
|
|
165
|
+
`organizationId="${organizationId || "your-org-id"}"`,
|
|
166
|
+
amount ? `amount={${amount}}` : null,
|
|
167
|
+
buttonBgColor ? `buttonBgColor="${buttonBgColor}"` : null,
|
|
168
|
+
pageBgColor ? `pageBgColor="${pageBgColor}"` : null,
|
|
169
|
+
callbackUrl ? `callbackUrl="${callbackUrl}"` : null,
|
|
170
|
+
buttonText ? `buttonText="${buttonText}"` : null,
|
|
171
|
+
checkoutBaseUrl ? `checkoutBaseUrl="${checkoutBaseUrl}"` : null
|
|
172
|
+
].filter(Boolean).join("\n ");
|
|
173
|
+
return `import { PayButton } from '@loofta/pay-sdk';
|
|
174
|
+
|
|
175
|
+
<PayButton
|
|
176
|
+
${propsStr}
|
|
177
|
+
onSuccess={(paymentId) => {
|
|
178
|
+
console.log('Payment completed:', paymentId);
|
|
179
|
+
}}
|
|
180
|
+
/>`;
|
|
181
|
+
}
|
|
182
|
+
function generateScriptEmbed(props) {
|
|
183
|
+
const { organizationId, amount, buttonBgColor, pageBgColor, callbackUrl, buttonText, checkoutBaseUrl } = props;
|
|
184
|
+
const base = checkoutBaseUrl || "https://pay.loofta.com";
|
|
185
|
+
return `<!-- Loofta Pay Button -->
|
|
186
|
+
<script src="${base}/sdk/loofta-pay.js"></script>
|
|
187
|
+
<div
|
|
188
|
+
id="loofta-pay-button"
|
|
189
|
+
data-organization-id="${organizationId || "your-org-id"}"
|
|
190
|
+
data-checkout-base-url="${base}"
|
|
191
|
+
${amount ? `data-amount="${amount}"` : ""}
|
|
192
|
+
${buttonBgColor ? `data-button-bg-color="${buttonBgColor}"` : ""}
|
|
193
|
+
${pageBgColor ? `data-page-bg-color="${pageBgColor}"` : ""}
|
|
194
|
+
${buttonText ? `data-button-text="${buttonText}"` : ""}
|
|
195
|
+
${callbackUrl ? `data-callback="${callbackUrl}"` : ""}
|
|
196
|
+
></div>
|
|
197
|
+
<script>
|
|
198
|
+
LooftaPay.mount('#loofta-pay-button');
|
|
199
|
+
</script>`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
exports.PayButton = PayButton;
|
|
203
|
+
exports.generateEmbedCode = generateEmbedCode;
|
|
204
|
+
exports.generateScriptEmbed = generateScriptEmbed;
|
|
205
|
+
//# sourceMappingURL=index.js.map
|
|
206
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/PayButton.tsx"],"names":["jsxs","jsx","useState","useCallback","Fragment"],"mappings":"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","file":"index.js","sourcesContent":["import { useState, useCallback } from 'react';\n\nexport interface PayButtonProps {\n /**\n * Organization ID (required). Must be set up with Loofta — your destination wallet, network, and token are configured in your organization.\n * If you don't have an organization ID, contact Loofta to get one.\n */\n organizationId: string;\n /** Payment amount in USD (optional) */\n amount?: number | string;\n /** Button background color (optional) */\n buttonBgColor?: string;\n /** Checkout page background color (optional - falls back to org settings) */\n pageBgColor?: string;\n /** @deprecated Use buttonBgColor instead */\n bgColor?: string;\n /** Callback URL after payment (optional) */\n callbackUrl?: string;\n /** Callback function after payment (optional) */\n onSuccess?: (paymentId: string) => void;\n /** Button text (optional) */\n buttonText?: string;\n /** Success text shown after payment (optional) */\n successText?: string;\n /** Custom className for button styling */\n className?: string;\n /** Open checkout in new tab vs popup (default: popup) */\n openMode?: 'popup' | 'redirect' | 'tab';\n /** Disable button */\n disabled?: boolean;\n /**\n * Base URL of the Loofta Pay hosted checkout page. Checkout is always Loofta's — this is only needed if the button is embedded on a different domain than the default Loofta Pay app. Usually omit; default is used.\n */\n checkoutBaseUrl?: string;\n}\n\nconst DEFAULT_STYLE: React.CSSProperties = {\n background: 'linear-gradient(to right, #FF0F00, #EAB308)',\n color: '#ffffff',\n border: 'none',\n borderRadius: '12px',\n padding: '12px 24px',\n fontSize: '16px',\n fontWeight: 600,\n cursor: 'pointer',\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '8px',\n minWidth: '160px',\n transition: 'transform 0.2s, box-shadow 0.2s',\n boxShadow: '0 4px 14px rgba(255, 15, 0, 0.25)',\n};\n\nconst HOVER_STYLE: React.CSSProperties = {\n transform: 'scale(1.02)',\n boxShadow: '0 6px 20px rgba(255, 15, 0, 0.35)',\n};\n\nfunction LoaderIcon() {\n return (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" strokeOpacity={0.25} />\n <path\n d=\"M12 2a10 10 0 0 1 10 10\"\n style={{ transformOrigin: '12px 12px' }}\n >\n <animateTransform\n attributeName=\"transform\"\n type=\"rotate\"\n from=\"0 12 12\"\n to=\"360 12 12\"\n dur=\"0.8s\"\n repeatCount=\"indefinite\"\n />\n </path>\n </svg>\n );\n}\n\nfunction CheckIcon() {\n return (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n );\n}\n\nfunction PayIcon() {\n return (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\" />\n </svg>\n );\n}\n\n/**\n * Loofta Pay Button – opens your Loofta Pay checkout (same API as your app).\n */\nexport function PayButton({\n organizationId,\n amount,\n buttonBgColor,\n pageBgColor,\n bgColor,\n callbackUrl,\n onSuccess,\n buttonText = 'Pay with Loofta',\n successText = 'Paid Successfully',\n className,\n openMode = 'popup',\n disabled = false,\n checkoutBaseUrl,\n}: PayButtonProps) {\n const [loading, setLoading] = useState(false);\n const [paid, setPaid] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n\n const effectiveButtonBgColor = buttonBgColor || bgColor;\n\n const getCheckoutUrl = useCallback(() => {\n const defaultBase = 'https://pay.loofta.com';\n const base =\n checkoutBaseUrl != null && checkoutBaseUrl !== ''\n ? checkoutBaseUrl.replace(/\\/$/, '')\n : defaultBase;\n\n const path = base.endsWith('/checkout') ? base : `${base}/checkout`;\n const params = new URLSearchParams();\n params.set('organizationId', organizationId);\n if (amount) params.set('amount', String(amount));\n if (pageBgColor) params.set('bgColor', encodeURIComponent(pageBgColor));\n if (callbackUrl) params.set('callback', encodeURIComponent(callbackUrl));\n\n return `${path}?${params.toString()}`;\n }, [checkoutBaseUrl, organizationId, amount, pageBgColor, callbackUrl]);\n\n const handleClick = useCallback(() => {\n if (disabled || loading || paid) return;\n\n setLoading(true);\n const url = getCheckoutUrl();\n\n if (openMode === 'redirect') {\n window.location.href = url;\n return;\n }\n\n if (openMode === 'tab') {\n window.open(url, '_blank', 'noopener,noreferrer');\n setLoading(false);\n return;\n }\n\n const width = 500;\n const height = 700;\n const left = window.screenX + (window.outerWidth - width) / 2;\n const top = window.screenY + (window.outerHeight - height) / 2;\n\n const popup = window.open(\n url,\n 'loofta-pay-checkout',\n `width=${width},height=${height},left=${left},top=${top},scrollbars=yes,resizable=yes`\n );\n\n const handleMessage = (event: MessageEvent) => {\n if (event.data?.type === 'loofta-payment-success') {\n setLoading(false);\n setPaid(true);\n onSuccess?.(event.data.paymentId);\n popup?.close();\n window.removeEventListener('message', handleMessage);\n }\n };\n window.addEventListener('message', handleMessage);\n\n const checkClosed = setInterval(() => {\n if (popup?.closed) {\n clearInterval(checkClosed);\n setLoading(false);\n window.removeEventListener('message', handleMessage);\n }\n }, 500);\n }, [disabled, loading, paid, getCheckoutUrl, openMode, onSuccess]);\n\n const successStyle: React.CSSProperties = {\n ...DEFAULT_STYLE,\n background: 'linear-gradient(to right, #10B981, #059669)',\n boxShadow: '0 4px 14px rgba(16, 185, 129, 0.25)',\n cursor: 'default',\n };\n\n const buttonStyle: React.CSSProperties = {\n ...DEFAULT_STYLE,\n ...(effectiveButtonBgColor ? { background: effectiveButtonBgColor } : {}),\n ...(isHovered && !disabled && !paid ? HOVER_STYLE : {}),\n ...(disabled ? { opacity: 0.6, cursor: 'not-allowed' as const } : {}),\n };\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n disabled={disabled || loading || paid}\n className={className}\n style={className ? undefined : paid ? successStyle : buttonStyle}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n >\n {paid ? (\n <>\n <CheckIcon />\n {successText}\n </>\n ) : loading ? (\n <>\n <LoaderIcon />\n Processing...\n </>\n ) : (\n <>\n <PayIcon />\n {buttonText}\n </>\n )}\n </button>\n );\n}\n\nexport function generateEmbedCode(props: Partial<PayButtonProps>): string {\n const { organizationId, amount, buttonBgColor, pageBgColor, callbackUrl, buttonText, checkoutBaseUrl } = props;\n\n const propsStr = [\n `organizationId=\"${organizationId || 'your-org-id'}\"`,\n amount ? `amount={${amount}}` : null,\n buttonBgColor ? `buttonBgColor=\"${buttonBgColor}\"` : null,\n pageBgColor ? `pageBgColor=\"${pageBgColor}\"` : null,\n callbackUrl ? `callbackUrl=\"${callbackUrl}\"` : null,\n buttonText ? `buttonText=\"${buttonText}\"` : null,\n checkoutBaseUrl ? `checkoutBaseUrl=\"${checkoutBaseUrl}\"` : null,\n ]\n .filter(Boolean)\n .join('\\n ');\n\n return `import { PayButton } from '@loofta/pay-sdk';\n\n<PayButton\n ${propsStr}\n onSuccess={(paymentId) => {\n console.log('Payment completed:', paymentId);\n }}\n/>`;\n}\n\nexport function generateScriptEmbed(props: Partial<PayButtonProps>): string {\n const { organizationId, amount, buttonBgColor, pageBgColor, callbackUrl, buttonText, checkoutBaseUrl } = props;\n const base = checkoutBaseUrl || 'https://pay.loofta.com';\n\n return `<!-- Loofta Pay Button -->\n<script src=\"${base}/sdk/loofta-pay.js\"></script>\n<div \n id=\"loofta-pay-button\"\n data-organization-id=\"${organizationId || 'your-org-id'}\"\n data-checkout-base-url=\"${base}\"\n ${amount ? `data-amount=\"${amount}\"` : ''}\n ${buttonBgColor ? `data-button-bg-color=\"${buttonBgColor}\"` : ''}\n ${pageBgColor ? `data-page-bg-color=\"${pageBgColor}\"` : ''}\n ${buttonText ? `data-button-text=\"${buttonText}\"` : ''}\n ${callbackUrl ? `data-callback=\"${callbackUrl}\"` : ''}\n></div>\n<script>\n LooftaPay.mount('#loofta-pay-button');\n</script>`;\n}\n\nexport default PayButton;\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
// src/PayButton.tsx
|
|
5
|
+
var DEFAULT_STYLE = {
|
|
6
|
+
background: "linear-gradient(to right, #FF0F00, #EAB308)",
|
|
7
|
+
color: "#ffffff",
|
|
8
|
+
border: "none",
|
|
9
|
+
borderRadius: "12px",
|
|
10
|
+
padding: "12px 24px",
|
|
11
|
+
fontSize: "16px",
|
|
12
|
+
fontWeight: 600,
|
|
13
|
+
cursor: "pointer",
|
|
14
|
+
display: "inline-flex",
|
|
15
|
+
alignItems: "center",
|
|
16
|
+
justifyContent: "center",
|
|
17
|
+
gap: "8px",
|
|
18
|
+
minWidth: "160px",
|
|
19
|
+
transition: "transform 0.2s, box-shadow 0.2s",
|
|
20
|
+
boxShadow: "0 4px 14px rgba(255, 15, 0, 0.25)"
|
|
21
|
+
};
|
|
22
|
+
var HOVER_STYLE = {
|
|
23
|
+
transform: "scale(1.02)",
|
|
24
|
+
boxShadow: "0 6px 20px rgba(255, 15, 0, 0.35)"
|
|
25
|
+
};
|
|
26
|
+
function LoaderIcon() {
|
|
27
|
+
return /* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
|
|
28
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10", strokeOpacity: 0.25 }),
|
|
29
|
+
/* @__PURE__ */ jsx(
|
|
30
|
+
"path",
|
|
31
|
+
{
|
|
32
|
+
d: "M12 2a10 10 0 0 1 10 10",
|
|
33
|
+
style: { transformOrigin: "12px 12px" },
|
|
34
|
+
children: /* @__PURE__ */ jsx(
|
|
35
|
+
"animateTransform",
|
|
36
|
+
{
|
|
37
|
+
attributeName: "transform",
|
|
38
|
+
type: "rotate",
|
|
39
|
+
from: "0 12 12",
|
|
40
|
+
to: "360 12 12",
|
|
41
|
+
dur: "0.8s",
|
|
42
|
+
repeatCount: "indefinite"
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
] });
|
|
48
|
+
}
|
|
49
|
+
function CheckIcon() {
|
|
50
|
+
return /* @__PURE__ */ jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" }) });
|
|
51
|
+
}
|
|
52
|
+
function PayIcon() {
|
|
53
|
+
return /* @__PURE__ */ jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" }) });
|
|
54
|
+
}
|
|
55
|
+
function PayButton({
|
|
56
|
+
organizationId,
|
|
57
|
+
amount,
|
|
58
|
+
buttonBgColor,
|
|
59
|
+
pageBgColor,
|
|
60
|
+
bgColor,
|
|
61
|
+
callbackUrl,
|
|
62
|
+
onSuccess,
|
|
63
|
+
buttonText = "Pay with Loofta",
|
|
64
|
+
successText = "Paid Successfully",
|
|
65
|
+
className,
|
|
66
|
+
openMode = "popup",
|
|
67
|
+
disabled = false,
|
|
68
|
+
checkoutBaseUrl
|
|
69
|
+
}) {
|
|
70
|
+
const [loading, setLoading] = useState(false);
|
|
71
|
+
const [paid, setPaid] = useState(false);
|
|
72
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
73
|
+
const effectiveButtonBgColor = buttonBgColor || bgColor;
|
|
74
|
+
const getCheckoutUrl = useCallback(() => {
|
|
75
|
+
const defaultBase = "https://pay.loofta.com";
|
|
76
|
+
const base = checkoutBaseUrl != null && checkoutBaseUrl !== "" ? checkoutBaseUrl.replace(/\/$/, "") : defaultBase;
|
|
77
|
+
const path = base.endsWith("/checkout") ? base : `${base}/checkout`;
|
|
78
|
+
const params = new URLSearchParams();
|
|
79
|
+
params.set("organizationId", organizationId);
|
|
80
|
+
if (amount) params.set("amount", String(amount));
|
|
81
|
+
if (pageBgColor) params.set("bgColor", encodeURIComponent(pageBgColor));
|
|
82
|
+
if (callbackUrl) params.set("callback", encodeURIComponent(callbackUrl));
|
|
83
|
+
return `${path}?${params.toString()}`;
|
|
84
|
+
}, [checkoutBaseUrl, organizationId, amount, pageBgColor, callbackUrl]);
|
|
85
|
+
const handleClick = useCallback(() => {
|
|
86
|
+
if (disabled || loading || paid) return;
|
|
87
|
+
setLoading(true);
|
|
88
|
+
const url = getCheckoutUrl();
|
|
89
|
+
if (openMode === "redirect") {
|
|
90
|
+
window.location.href = url;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (openMode === "tab") {
|
|
94
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
95
|
+
setLoading(false);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const width = 500;
|
|
99
|
+
const height = 700;
|
|
100
|
+
const left = window.screenX + (window.outerWidth - width) / 2;
|
|
101
|
+
const top = window.screenY + (window.outerHeight - height) / 2;
|
|
102
|
+
const popup = window.open(
|
|
103
|
+
url,
|
|
104
|
+
"loofta-pay-checkout",
|
|
105
|
+
`width=${width},height=${height},left=${left},top=${top},scrollbars=yes,resizable=yes`
|
|
106
|
+
);
|
|
107
|
+
const handleMessage = (event) => {
|
|
108
|
+
if (event.data?.type === "loofta-payment-success") {
|
|
109
|
+
setLoading(false);
|
|
110
|
+
setPaid(true);
|
|
111
|
+
onSuccess?.(event.data.paymentId);
|
|
112
|
+
popup?.close();
|
|
113
|
+
window.removeEventListener("message", handleMessage);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
window.addEventListener("message", handleMessage);
|
|
117
|
+
const checkClosed = setInterval(() => {
|
|
118
|
+
if (popup?.closed) {
|
|
119
|
+
clearInterval(checkClosed);
|
|
120
|
+
setLoading(false);
|
|
121
|
+
window.removeEventListener("message", handleMessage);
|
|
122
|
+
}
|
|
123
|
+
}, 500);
|
|
124
|
+
}, [disabled, loading, paid, getCheckoutUrl, openMode, onSuccess]);
|
|
125
|
+
const successStyle = {
|
|
126
|
+
...DEFAULT_STYLE,
|
|
127
|
+
background: "linear-gradient(to right, #10B981, #059669)",
|
|
128
|
+
boxShadow: "0 4px 14px rgba(16, 185, 129, 0.25)",
|
|
129
|
+
cursor: "default"
|
|
130
|
+
};
|
|
131
|
+
const buttonStyle = {
|
|
132
|
+
...DEFAULT_STYLE,
|
|
133
|
+
...effectiveButtonBgColor ? { background: effectiveButtonBgColor } : {},
|
|
134
|
+
...isHovered && !disabled && !paid ? HOVER_STYLE : {},
|
|
135
|
+
...disabled ? { opacity: 0.6, cursor: "not-allowed" } : {}
|
|
136
|
+
};
|
|
137
|
+
return /* @__PURE__ */ jsx(
|
|
138
|
+
"button",
|
|
139
|
+
{
|
|
140
|
+
type: "button",
|
|
141
|
+
onClick: handleClick,
|
|
142
|
+
disabled: disabled || loading || paid,
|
|
143
|
+
className,
|
|
144
|
+
style: className ? void 0 : paid ? successStyle : buttonStyle,
|
|
145
|
+
onMouseEnter: () => setIsHovered(true),
|
|
146
|
+
onMouseLeave: () => setIsHovered(false),
|
|
147
|
+
children: paid ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
148
|
+
/* @__PURE__ */ jsx(CheckIcon, {}),
|
|
149
|
+
successText
|
|
150
|
+
] }) : loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
151
|
+
/* @__PURE__ */ jsx(LoaderIcon, {}),
|
|
152
|
+
"Processing..."
|
|
153
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
154
|
+
/* @__PURE__ */ jsx(PayIcon, {}),
|
|
155
|
+
buttonText
|
|
156
|
+
] })
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
function generateEmbedCode(props) {
|
|
161
|
+
const { organizationId, amount, buttonBgColor, pageBgColor, callbackUrl, buttonText, checkoutBaseUrl } = props;
|
|
162
|
+
const propsStr = [
|
|
163
|
+
`organizationId="${organizationId || "your-org-id"}"`,
|
|
164
|
+
amount ? `amount={${amount}}` : null,
|
|
165
|
+
buttonBgColor ? `buttonBgColor="${buttonBgColor}"` : null,
|
|
166
|
+
pageBgColor ? `pageBgColor="${pageBgColor}"` : null,
|
|
167
|
+
callbackUrl ? `callbackUrl="${callbackUrl}"` : null,
|
|
168
|
+
buttonText ? `buttonText="${buttonText}"` : null,
|
|
169
|
+
checkoutBaseUrl ? `checkoutBaseUrl="${checkoutBaseUrl}"` : null
|
|
170
|
+
].filter(Boolean).join("\n ");
|
|
171
|
+
return `import { PayButton } from '@loofta/pay-sdk';
|
|
172
|
+
|
|
173
|
+
<PayButton
|
|
174
|
+
${propsStr}
|
|
175
|
+
onSuccess={(paymentId) => {
|
|
176
|
+
console.log('Payment completed:', paymentId);
|
|
177
|
+
}}
|
|
178
|
+
/>`;
|
|
179
|
+
}
|
|
180
|
+
function generateScriptEmbed(props) {
|
|
181
|
+
const { organizationId, amount, buttonBgColor, pageBgColor, callbackUrl, buttonText, checkoutBaseUrl } = props;
|
|
182
|
+
const base = checkoutBaseUrl || "https://pay.loofta.com";
|
|
183
|
+
return `<!-- Loofta Pay Button -->
|
|
184
|
+
<script src="${base}/sdk/loofta-pay.js"></script>
|
|
185
|
+
<div
|
|
186
|
+
id="loofta-pay-button"
|
|
187
|
+
data-organization-id="${organizationId || "your-org-id"}"
|
|
188
|
+
data-checkout-base-url="${base}"
|
|
189
|
+
${amount ? `data-amount="${amount}"` : ""}
|
|
190
|
+
${buttonBgColor ? `data-button-bg-color="${buttonBgColor}"` : ""}
|
|
191
|
+
${pageBgColor ? `data-page-bg-color="${pageBgColor}"` : ""}
|
|
192
|
+
${buttonText ? `data-button-text="${buttonText}"` : ""}
|
|
193
|
+
${callbackUrl ? `data-callback="${callbackUrl}"` : ""}
|
|
194
|
+
></div>
|
|
195
|
+
<script>
|
|
196
|
+
LooftaPay.mount('#loofta-pay-button');
|
|
197
|
+
</script>`;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export { PayButton, generateEmbedCode, generateScriptEmbed };
|
|
201
|
+
//# sourceMappingURL=index.mjs.map
|
|
202
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/PayButton.tsx"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import { useState, useCallback } from 'react';\n\nexport interface PayButtonProps {\n /**\n * Organization ID (required). Must be set up with Loofta — your destination wallet, network, and token are configured in your organization.\n * If you don't have an organization ID, contact Loofta to get one.\n */\n organizationId: string;\n /** Payment amount in USD (optional) */\n amount?: number | string;\n /** Button background color (optional) */\n buttonBgColor?: string;\n /** Checkout page background color (optional - falls back to org settings) */\n pageBgColor?: string;\n /** @deprecated Use buttonBgColor instead */\n bgColor?: string;\n /** Callback URL after payment (optional) */\n callbackUrl?: string;\n /** Callback function after payment (optional) */\n onSuccess?: (paymentId: string) => void;\n /** Button text (optional) */\n buttonText?: string;\n /** Success text shown after payment (optional) */\n successText?: string;\n /** Custom className for button styling */\n className?: string;\n /** Open checkout in new tab vs popup (default: popup) */\n openMode?: 'popup' | 'redirect' | 'tab';\n /** Disable button */\n disabled?: boolean;\n /**\n * Base URL of the Loofta Pay hosted checkout page. Checkout is always Loofta's — this is only needed if the button is embedded on a different domain than the default Loofta Pay app. Usually omit; default is used.\n */\n checkoutBaseUrl?: string;\n}\n\nconst DEFAULT_STYLE: React.CSSProperties = {\n background: 'linear-gradient(to right, #FF0F00, #EAB308)',\n color: '#ffffff',\n border: 'none',\n borderRadius: '12px',\n padding: '12px 24px',\n fontSize: '16px',\n fontWeight: 600,\n cursor: 'pointer',\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '8px',\n minWidth: '160px',\n transition: 'transform 0.2s, box-shadow 0.2s',\n boxShadow: '0 4px 14px rgba(255, 15, 0, 0.25)',\n};\n\nconst HOVER_STYLE: React.CSSProperties = {\n transform: 'scale(1.02)',\n boxShadow: '0 6px 20px rgba(255, 15, 0, 0.35)',\n};\n\nfunction LoaderIcon() {\n return (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" strokeOpacity={0.25} />\n <path\n d=\"M12 2a10 10 0 0 1 10 10\"\n style={{ transformOrigin: '12px 12px' }}\n >\n <animateTransform\n attributeName=\"transform\"\n type=\"rotate\"\n from=\"0 12 12\"\n to=\"360 12 12\"\n dur=\"0.8s\"\n repeatCount=\"indefinite\"\n />\n </path>\n </svg>\n );\n}\n\nfunction CheckIcon() {\n return (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n );\n}\n\nfunction PayIcon() {\n return (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\" />\n </svg>\n );\n}\n\n/**\n * Loofta Pay Button – opens your Loofta Pay checkout (same API as your app).\n */\nexport function PayButton({\n organizationId,\n amount,\n buttonBgColor,\n pageBgColor,\n bgColor,\n callbackUrl,\n onSuccess,\n buttonText = 'Pay with Loofta',\n successText = 'Paid Successfully',\n className,\n openMode = 'popup',\n disabled = false,\n checkoutBaseUrl,\n}: PayButtonProps) {\n const [loading, setLoading] = useState(false);\n const [paid, setPaid] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n\n const effectiveButtonBgColor = buttonBgColor || bgColor;\n\n const getCheckoutUrl = useCallback(() => {\n const defaultBase = 'https://pay.loofta.com';\n const base =\n checkoutBaseUrl != null && checkoutBaseUrl !== ''\n ? checkoutBaseUrl.replace(/\\/$/, '')\n : defaultBase;\n\n const path = base.endsWith('/checkout') ? base : `${base}/checkout`;\n const params = new URLSearchParams();\n params.set('organizationId', organizationId);\n if (amount) params.set('amount', String(amount));\n if (pageBgColor) params.set('bgColor', encodeURIComponent(pageBgColor));\n if (callbackUrl) params.set('callback', encodeURIComponent(callbackUrl));\n\n return `${path}?${params.toString()}`;\n }, [checkoutBaseUrl, organizationId, amount, pageBgColor, callbackUrl]);\n\n const handleClick = useCallback(() => {\n if (disabled || loading || paid) return;\n\n setLoading(true);\n const url = getCheckoutUrl();\n\n if (openMode === 'redirect') {\n window.location.href = url;\n return;\n }\n\n if (openMode === 'tab') {\n window.open(url, '_blank', 'noopener,noreferrer');\n setLoading(false);\n return;\n }\n\n const width = 500;\n const height = 700;\n const left = window.screenX + (window.outerWidth - width) / 2;\n const top = window.screenY + (window.outerHeight - height) / 2;\n\n const popup = window.open(\n url,\n 'loofta-pay-checkout',\n `width=${width},height=${height},left=${left},top=${top},scrollbars=yes,resizable=yes`\n );\n\n const handleMessage = (event: MessageEvent) => {\n if (event.data?.type === 'loofta-payment-success') {\n setLoading(false);\n setPaid(true);\n onSuccess?.(event.data.paymentId);\n popup?.close();\n window.removeEventListener('message', handleMessage);\n }\n };\n window.addEventListener('message', handleMessage);\n\n const checkClosed = setInterval(() => {\n if (popup?.closed) {\n clearInterval(checkClosed);\n setLoading(false);\n window.removeEventListener('message', handleMessage);\n }\n }, 500);\n }, [disabled, loading, paid, getCheckoutUrl, openMode, onSuccess]);\n\n const successStyle: React.CSSProperties = {\n ...DEFAULT_STYLE,\n background: 'linear-gradient(to right, #10B981, #059669)',\n boxShadow: '0 4px 14px rgba(16, 185, 129, 0.25)',\n cursor: 'default',\n };\n\n const buttonStyle: React.CSSProperties = {\n ...DEFAULT_STYLE,\n ...(effectiveButtonBgColor ? { background: effectiveButtonBgColor } : {}),\n ...(isHovered && !disabled && !paid ? HOVER_STYLE : {}),\n ...(disabled ? { opacity: 0.6, cursor: 'not-allowed' as const } : {}),\n };\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n disabled={disabled || loading || paid}\n className={className}\n style={className ? undefined : paid ? successStyle : buttonStyle}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n >\n {paid ? (\n <>\n <CheckIcon />\n {successText}\n </>\n ) : loading ? (\n <>\n <LoaderIcon />\n Processing...\n </>\n ) : (\n <>\n <PayIcon />\n {buttonText}\n </>\n )}\n </button>\n );\n}\n\nexport function generateEmbedCode(props: Partial<PayButtonProps>): string {\n const { organizationId, amount, buttonBgColor, pageBgColor, callbackUrl, buttonText, checkoutBaseUrl } = props;\n\n const propsStr = [\n `organizationId=\"${organizationId || 'your-org-id'}\"`,\n amount ? `amount={${amount}}` : null,\n buttonBgColor ? `buttonBgColor=\"${buttonBgColor}\"` : null,\n pageBgColor ? `pageBgColor=\"${pageBgColor}\"` : null,\n callbackUrl ? `callbackUrl=\"${callbackUrl}\"` : null,\n buttonText ? `buttonText=\"${buttonText}\"` : null,\n checkoutBaseUrl ? `checkoutBaseUrl=\"${checkoutBaseUrl}\"` : null,\n ]\n .filter(Boolean)\n .join('\\n ');\n\n return `import { PayButton } from '@loofta/pay-sdk';\n\n<PayButton\n ${propsStr}\n onSuccess={(paymentId) => {\n console.log('Payment completed:', paymentId);\n }}\n/>`;\n}\n\nexport function generateScriptEmbed(props: Partial<PayButtonProps>): string {\n const { organizationId, amount, buttonBgColor, pageBgColor, callbackUrl, buttonText, checkoutBaseUrl } = props;\n const base = checkoutBaseUrl || 'https://pay.loofta.com';\n\n return `<!-- Loofta Pay Button -->\n<script src=\"${base}/sdk/loofta-pay.js\"></script>\n<div \n id=\"loofta-pay-button\"\n data-organization-id=\"${organizationId || 'your-org-id'}\"\n data-checkout-base-url=\"${base}\"\n ${amount ? `data-amount=\"${amount}\"` : ''}\n ${buttonBgColor ? `data-button-bg-color=\"${buttonBgColor}\"` : ''}\n ${pageBgColor ? `data-page-bg-color=\"${pageBgColor}\"` : ''}\n ${buttonText ? `data-button-text=\"${buttonText}\"` : ''}\n ${callbackUrl ? `data-callback=\"${callbackUrl}\"` : ''}\n></div>\n<script>\n LooftaPay.mount('#loofta-pay-button');\n</script>`;\n}\n\nexport default PayButton;\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@loofta/pay-sdk",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Loofta Pay Button SDK - embeddable payment button that opens Loofta Pay checkout",
|
|
5
|
+
"author": "Loofta",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"require": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"dev": "tsup --watch"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"react": ">=18.0.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/react": "^18.0.0",
|
|
30
|
+
"react": "^18.0.0",
|
|
31
|
+
"tsup": "^8.0.0",
|
|
32
|
+
"typescript": "^5.0.0"
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/loofta/loofta-swap.git"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"loofta",
|
|
40
|
+
"pay",
|
|
41
|
+
"payment",
|
|
42
|
+
"checkout",
|
|
43
|
+
"crypto",
|
|
44
|
+
"multichain"
|
|
45
|
+
],
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
}
|
|
49
|
+
}
|