@payotex.com/checkout 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,143 @@
1
+ # @payotex/checkout
2
+
3
+ Client-side checkout widget for Payotex crypto payments. Provides a React component and vanilla JS function to embed the Payotex payment widget on any website.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @payotex/checkout
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ### React
14
+
15
+ ```tsx
16
+ import { PayotexCheckout } from '@payotex/checkout';
17
+
18
+ // Session mode (recommended for server-side session creation)
19
+ function CheckoutPage({ sessionId }: { sessionId: string }) {
20
+ return (
21
+ <PayotexCheckout
22
+ sessionId={sessionId}
23
+ onLoad={() => console.log('Widget loaded')}
24
+ onError={(err) => console.error('Widget error:', err)}
25
+ />
26
+ );
27
+ }
28
+
29
+ // Publishable key mode (client-side session creation)
30
+ function QuickCheckout() {
31
+ return (
32
+ <PayotexCheckout
33
+ publishableKey="pk_live_your_key_here"
34
+ amount="49.99"
35
+ currency="USD"
36
+ buttonText="Pay with Crypto"
37
+ />
38
+ );
39
+ }
40
+ ```
41
+
42
+ ### Vanilla JavaScript
43
+
44
+ ```js
45
+ import { loadPayotexCheckout } from '@payotex/checkout';
46
+
47
+ const container = document.getElementById('checkout-container');
48
+
49
+ const cleanup = loadPayotexCheckout(container, {
50
+ sessionId: 'your-session-id',
51
+ onLoad: () => console.log('Widget loaded'),
52
+ onError: (err) => console.error('Error:', err),
53
+ });
54
+
55
+ // Call cleanup() when you want to remove the widget
56
+ ```
57
+
58
+ ## Framework Examples
59
+
60
+ ### Next.js
61
+
62
+ ```tsx
63
+ 'use client';
64
+ import { PayotexCheckout } from '@payotex/checkout';
65
+
66
+ export default function CheckoutPage({ params }: { params: { sessionId: string } }) {
67
+ return (
68
+ <div>
69
+ <h1>Complete Your Payment</h1>
70
+ <PayotexCheckout
71
+ sessionId={params.sessionId}
72
+ onError={(err) => console.error(err)}
73
+ />
74
+ </div>
75
+ );
76
+ }
77
+ ```
78
+
79
+ ### Plain HTML (via CDN / bundler)
80
+
81
+ ```html
82
+ <div id="payotex-checkout"></div>
83
+
84
+ <script type="module">
85
+ import { loadPayotexCheckout } from '@payotex/checkout';
86
+
87
+ loadPayotexCheckout(document.getElementById('payotex-checkout'), {
88
+ publishableKey: 'pk_live_your_key_here',
89
+ amount: '25.00',
90
+ currency: 'USD',
91
+ });
92
+ </script>
93
+ ```
94
+
95
+ ## Props / Options Reference
96
+
97
+ | Option | Type | Required | Default | Description |
98
+ |---|---|---|---|---|
99
+ | `publishableKey` | `string` | Conditional | — | Your publishable key (`pk_live_...`). Required if no `sessionId`. |
100
+ | `sessionId` | `string` | Conditional | — | Pre-created checkout session ID. Required if no `publishableKey`. |
101
+ | `amount` | `string \| number` | Conditional | — | Payment amount. Required when using `publishableKey` without `sessionId`. |
102
+ | `currency` | `string` | No | `"USD"` | Currency code: `USD`, `EUR`, `GBP`. |
103
+ | `buttonText` | `string` | No | `"Pay with Crypto"` | Custom text for the payment button. |
104
+ | `baseUrl` | `string` | No | `"https://payotex.com"` | Base URL for the Payotex API. |
105
+ | `onLoad` | `() => void` | No | — | Called when the widget script has loaded. |
106
+ | `onError` | `(error: Error) => void` | No | — | Called on errors (load failures, validation errors). |
107
+
108
+ ### React-only Props
109
+
110
+ | Prop | Type | Description |
111
+ |---|---|---|
112
+ | `className` | `string` | Additional CSS class for the container div. |
113
+ | `style` | `React.CSSProperties` | Inline styles for the container div. |
114
+
115
+ ## Security
116
+
117
+ - **Never use your secret key (`sk_live_...`) in client-side code.** The SDK will throw a `SECURITY ERROR` if a secret key is detected.
118
+ - Use your **publishable key** (`pk_live_...`) for client-side integrations.
119
+ - Secret keys must only be used server-side with the `@payotex/node` SDK.
120
+ - For maximum security, create checkout sessions server-side and pass the `sessionId` to the client.
121
+
122
+ ## TypeScript Support
123
+
124
+ This package includes full TypeScript type definitions. All types are exported:
125
+
126
+ ```typescript
127
+ import type { PayotexCheckoutOptions, PayotexCheckoutProps } from '@payotex/checkout';
128
+ ```
129
+
130
+ ## How It Works
131
+
132
+ The SDK dynamically injects the Payotex `widget.js` script with the appropriate `data-*` attributes. The widget script handles the entire checkout UI including:
133
+
134
+ - Chain/asset selection
135
+ - Quote generation
136
+ - QR code display
137
+ - Payment address and memo
138
+ - Real-time transaction tracking
139
+ - Payment confirmation
140
+
141
+ ## License
142
+
143
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // packages/checkout/src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ PayotexCheckout: () => PayotexCheckout,
24
+ loadPayotexCheckout: () => loadPayotexCheckout
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // packages/checkout/src/react.tsx
29
+ var import_react = require("react");
30
+
31
+ // packages/checkout/src/loader.ts
32
+ var DEFAULT_BASE_URL = "https://payotex.com";
33
+ function validateOptions(options) {
34
+ if (options.publishableKey && options.publishableKey.startsWith("sk_live_")) {
35
+ throw new Error(
36
+ "SECURITY ERROR: Never use your secret key (sk_live_) in client-side code. Use your publishable key (pk_live_) instead. Secret keys must only be used server-side."
37
+ );
38
+ }
39
+ if (!options.publishableKey && !options.sessionId) {
40
+ throw new Error("Either publishableKey or sessionId is required");
41
+ }
42
+ if (options.publishableKey && !options.publishableKey.startsWith("pk_live_")) {
43
+ throw new Error("Invalid publishable key format. Must start with pk_live_");
44
+ }
45
+ if (options.publishableKey && !options.sessionId) {
46
+ if (options.amount === void 0 || options.amount === null || options.amount === "") {
47
+ throw new Error("amount is required when using publishableKey");
48
+ }
49
+ const numAmount = typeof options.amount === "number" ? options.amount : parseFloat(String(options.amount));
50
+ if (isNaN(numAmount) || numAmount <= 0) {
51
+ throw new Error("amount must be a positive number");
52
+ }
53
+ }
54
+ if (options.sessionId && typeof options.sessionId !== "string") {
55
+ throw new Error("sessionId must be a string");
56
+ }
57
+ }
58
+ function loadPayotexCheckout(container, options) {
59
+ validateOptions(options);
60
+ const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
61
+ const script = document.createElement("script");
62
+ script.src = `${baseUrl}/widget.js`;
63
+ if (options.sessionId) {
64
+ script.setAttribute("data-session-id", options.sessionId);
65
+ }
66
+ if (options.publishableKey) {
67
+ script.setAttribute("data-key", options.publishableKey);
68
+ }
69
+ if (options.amount !== void 0) {
70
+ script.setAttribute("data-amount", String(options.amount));
71
+ }
72
+ if (options.currency) {
73
+ script.setAttribute("data-currency", options.currency);
74
+ }
75
+ if (options.buttonText) {
76
+ script.setAttribute("data-button-text", options.buttonText);
77
+ }
78
+ script.onload = () => {
79
+ var _a;
80
+ (_a = options.onLoad) == null ? void 0 : _a.call(options);
81
+ };
82
+ script.onerror = () => {
83
+ var _a;
84
+ const err = new Error("Failed to load Payotex checkout widget");
85
+ (_a = options.onError) == null ? void 0 : _a.call(options, err);
86
+ };
87
+ container.appendChild(script);
88
+ return () => {
89
+ script.remove();
90
+ const widgetContainers = container.querySelectorAll('[id^="payotex-widget-"]');
91
+ widgetContainers.forEach((el) => el.remove());
92
+ };
93
+ }
94
+
95
+ // packages/checkout/src/react.tsx
96
+ function PayotexCheckout(props) {
97
+ const { className, style, ...options } = props;
98
+ const containerRef = (0, import_react.useRef)(null);
99
+ const cleanupRef = (0, import_react.useRef)(null);
100
+ (0, import_react.useEffect)(() => {
101
+ var _a;
102
+ const container = containerRef.current;
103
+ if (!container) return;
104
+ if (cleanupRef.current) {
105
+ cleanupRef.current();
106
+ cleanupRef.current = null;
107
+ }
108
+ try {
109
+ cleanupRef.current = loadPayotexCheckout(container, options);
110
+ } catch (err) {
111
+ (_a = options.onError) == null ? void 0 : _a.call(options, err instanceof Error ? err : new Error(String(err)));
112
+ }
113
+ return () => {
114
+ if (cleanupRef.current) {
115
+ cleanupRef.current();
116
+ cleanupRef.current = null;
117
+ }
118
+ };
119
+ }, [
120
+ options.publishableKey,
121
+ options.sessionId,
122
+ options.amount,
123
+ options.currency,
124
+ options.buttonText,
125
+ options.baseUrl
126
+ ]);
127
+ return /* @__PURE__ */ React.createElement("div", { ref: containerRef, className, style, "data-testid": "payotex-checkout-container" });
128
+ }
129
+ // Annotate the CommonJS export names for ESM import in node:
130
+ 0 && (module.exports = {
131
+ PayotexCheckout,
132
+ loadPayotexCheckout
133
+ });
@@ -0,0 +1,32 @@
1
+ import * as react from 'react';
2
+
3
+ interface PayotexCheckoutOptions {
4
+ /** Your publishable key (pk_live_...). Required if no sessionId. */
5
+ publishableKey?: string;
6
+ /** Pre-created checkout session ID. Required if no publishableKey. */
7
+ sessionId?: string;
8
+ /** Payment amount (required when using publishableKey). */
9
+ amount?: string | number;
10
+ /** Currency code - USD, EUR, GBP (default: "USD"). */
11
+ currency?: string;
12
+ /** Custom button text (default: "Pay with Crypto"). */
13
+ buttonText?: string;
14
+ /** Base URL for Payotex API (default: "https://payotex.com"). */
15
+ baseUrl?: string;
16
+ /** Called when the widget script has loaded. */
17
+ onLoad?: () => void;
18
+ /** Called on errors. */
19
+ onError?: (error: Error) => void;
20
+ }
21
+
22
+ type PayotexCheckoutProps = PayotexCheckoutOptions & {
23
+ /** Additional className for the container div */
24
+ className?: string;
25
+ /** Additional inline styles for the container div */
26
+ style?: React.CSSProperties;
27
+ };
28
+ declare function PayotexCheckout(props: PayotexCheckoutProps): react.JSX.Element;
29
+
30
+ declare function loadPayotexCheckout(container: HTMLElement, options: PayotexCheckoutOptions): () => void;
31
+
32
+ export { PayotexCheckout, type PayotexCheckoutOptions, type PayotexCheckoutProps, loadPayotexCheckout };
@@ -0,0 +1,32 @@
1
+ import * as react from 'react';
2
+
3
+ interface PayotexCheckoutOptions {
4
+ /** Your publishable key (pk_live_...). Required if no sessionId. */
5
+ publishableKey?: string;
6
+ /** Pre-created checkout session ID. Required if no publishableKey. */
7
+ sessionId?: string;
8
+ /** Payment amount (required when using publishableKey). */
9
+ amount?: string | number;
10
+ /** Currency code - USD, EUR, GBP (default: "USD"). */
11
+ currency?: string;
12
+ /** Custom button text (default: "Pay with Crypto"). */
13
+ buttonText?: string;
14
+ /** Base URL for Payotex API (default: "https://payotex.com"). */
15
+ baseUrl?: string;
16
+ /** Called when the widget script has loaded. */
17
+ onLoad?: () => void;
18
+ /** Called on errors. */
19
+ onError?: (error: Error) => void;
20
+ }
21
+
22
+ type PayotexCheckoutProps = PayotexCheckoutOptions & {
23
+ /** Additional className for the container div */
24
+ className?: string;
25
+ /** Additional inline styles for the container div */
26
+ style?: React.CSSProperties;
27
+ };
28
+ declare function PayotexCheckout(props: PayotexCheckoutProps): react.JSX.Element;
29
+
30
+ declare function loadPayotexCheckout(container: HTMLElement, options: PayotexCheckoutOptions): () => void;
31
+
32
+ export { PayotexCheckout, type PayotexCheckoutOptions, type PayotexCheckoutProps, loadPayotexCheckout };
package/dist/index.js ADDED
@@ -0,0 +1,105 @@
1
+ // packages/checkout/src/react.tsx
2
+ import { useEffect, useRef } from "react";
3
+
4
+ // packages/checkout/src/loader.ts
5
+ var DEFAULT_BASE_URL = "https://payotex.com";
6
+ function validateOptions(options) {
7
+ if (options.publishableKey && options.publishableKey.startsWith("sk_live_")) {
8
+ throw new Error(
9
+ "SECURITY ERROR: Never use your secret key (sk_live_) in client-side code. Use your publishable key (pk_live_) instead. Secret keys must only be used server-side."
10
+ );
11
+ }
12
+ if (!options.publishableKey && !options.sessionId) {
13
+ throw new Error("Either publishableKey or sessionId is required");
14
+ }
15
+ if (options.publishableKey && !options.publishableKey.startsWith("pk_live_")) {
16
+ throw new Error("Invalid publishable key format. Must start with pk_live_");
17
+ }
18
+ if (options.publishableKey && !options.sessionId) {
19
+ if (options.amount === void 0 || options.amount === null || options.amount === "") {
20
+ throw new Error("amount is required when using publishableKey");
21
+ }
22
+ const numAmount = typeof options.amount === "number" ? options.amount : parseFloat(String(options.amount));
23
+ if (isNaN(numAmount) || numAmount <= 0) {
24
+ throw new Error("amount must be a positive number");
25
+ }
26
+ }
27
+ if (options.sessionId && typeof options.sessionId !== "string") {
28
+ throw new Error("sessionId must be a string");
29
+ }
30
+ }
31
+ function loadPayotexCheckout(container, options) {
32
+ validateOptions(options);
33
+ const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
34
+ const script = document.createElement("script");
35
+ script.src = `${baseUrl}/widget.js`;
36
+ if (options.sessionId) {
37
+ script.setAttribute("data-session-id", options.sessionId);
38
+ }
39
+ if (options.publishableKey) {
40
+ script.setAttribute("data-key", options.publishableKey);
41
+ }
42
+ if (options.amount !== void 0) {
43
+ script.setAttribute("data-amount", String(options.amount));
44
+ }
45
+ if (options.currency) {
46
+ script.setAttribute("data-currency", options.currency);
47
+ }
48
+ if (options.buttonText) {
49
+ script.setAttribute("data-button-text", options.buttonText);
50
+ }
51
+ script.onload = () => {
52
+ var _a;
53
+ (_a = options.onLoad) == null ? void 0 : _a.call(options);
54
+ };
55
+ script.onerror = () => {
56
+ var _a;
57
+ const err = new Error("Failed to load Payotex checkout widget");
58
+ (_a = options.onError) == null ? void 0 : _a.call(options, err);
59
+ };
60
+ container.appendChild(script);
61
+ return () => {
62
+ script.remove();
63
+ const widgetContainers = container.querySelectorAll('[id^="payotex-widget-"]');
64
+ widgetContainers.forEach((el) => el.remove());
65
+ };
66
+ }
67
+
68
+ // packages/checkout/src/react.tsx
69
+ function PayotexCheckout(props) {
70
+ const { className, style, ...options } = props;
71
+ const containerRef = useRef(null);
72
+ const cleanupRef = useRef(null);
73
+ useEffect(() => {
74
+ var _a;
75
+ const container = containerRef.current;
76
+ if (!container) return;
77
+ if (cleanupRef.current) {
78
+ cleanupRef.current();
79
+ cleanupRef.current = null;
80
+ }
81
+ try {
82
+ cleanupRef.current = loadPayotexCheckout(container, options);
83
+ } catch (err) {
84
+ (_a = options.onError) == null ? void 0 : _a.call(options, err instanceof Error ? err : new Error(String(err)));
85
+ }
86
+ return () => {
87
+ if (cleanupRef.current) {
88
+ cleanupRef.current();
89
+ cleanupRef.current = null;
90
+ }
91
+ };
92
+ }, [
93
+ options.publishableKey,
94
+ options.sessionId,
95
+ options.amount,
96
+ options.currency,
97
+ options.buttonText,
98
+ options.baseUrl
99
+ ]);
100
+ return /* @__PURE__ */ React.createElement("div", { ref: containerRef, className, style, "data-testid": "payotex-checkout-container" });
101
+ }
102
+ export {
103
+ PayotexCheckout,
104
+ loadPayotexCheckout
105
+ };
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@payotex.com/checkout",
3
+ "version": "1.0.0",
4
+ "description": "Client-side checkout widget for Payotex crypto payments",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": ["dist"],
16
+ "scripts": {
17
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean --external react",
18
+ "test": "node --experimental-vm-modules ../../node_modules/.bin/vitest run"
19
+ },
20
+ "keywords": ["payotex", "crypto", "payments", "checkout", "widget", "react", "cross-chain", "bitcoin", "ethereum"],
21
+ "license": "MIT",
22
+ "peerDependencies": {
23
+ "react": ">=17.0.0"
24
+ },
25
+ "peerDependenciesMeta": {
26
+ "react": {
27
+ "optional": true
28
+ }
29
+ },
30
+ "devDependencies": {
31
+ "tsup": "^8.0.0",
32
+ "typescript": "^5.0.0",
33
+ "vitest": "^1.0.0",
34
+ "react": "^18.0.0",
35
+ "@types/react": "^18.0.0"
36
+ }
37
+ }