@sintecinformatik/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 +106 -0
- package/dist/index.d.mts +47 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +120 -0
- package/dist/index.mjs +92 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# @sintec/checkout
|
|
2
|
+
|
|
3
|
+
React components for integrating Sintec License Server checkout into your product website.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @sintec/checkout
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
> **Note**: Package not yet published to npm. For now, copy the source from this directory or use a local path reference.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### CheckoutButton Component
|
|
16
|
+
|
|
17
|
+
The simplest way to add a buy button:
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { CheckoutButton } from "@sintec/checkout";
|
|
21
|
+
|
|
22
|
+
function BuyPage() {
|
|
23
|
+
return (
|
|
24
|
+
<CheckoutButton
|
|
25
|
+
productSlug="your-product"
|
|
26
|
+
licenseType="perpetual"
|
|
27
|
+
customerEmail="user@example.com"
|
|
28
|
+
className="buy-button"
|
|
29
|
+
>
|
|
30
|
+
Buy License - $99
|
|
31
|
+
</CheckoutButton>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### useCheckout Hook
|
|
37
|
+
|
|
38
|
+
For custom UI implementations:
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { useCheckout } from "@sintec/checkout";
|
|
42
|
+
|
|
43
|
+
function CustomCheckout() {
|
|
44
|
+
const { startCheckout, isLoading, error } = useCheckout();
|
|
45
|
+
|
|
46
|
+
const handleBuy = async () => {
|
|
47
|
+
await startCheckout({
|
|
48
|
+
productSlug: "your-product",
|
|
49
|
+
licenseType: "perpetual",
|
|
50
|
+
customerEmail: "user@example.com",
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div>
|
|
56
|
+
<button onClick={handleBuy} disabled={isLoading}>
|
|
57
|
+
{isLoading ? "Processing..." : "Purchase"}
|
|
58
|
+
</button>
|
|
59
|
+
{error && <p className="error">{error}</p>}
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## API Reference
|
|
66
|
+
|
|
67
|
+
### CheckoutButton Props
|
|
68
|
+
|
|
69
|
+
| Prop | Type | Required | Description |
|
|
70
|
+
|------|------|----------|-------------|
|
|
71
|
+
| `productSlug` | `string` | Yes | Product identifier |
|
|
72
|
+
| `licenseType` | `"perpetual" \| "monthly" \| "yearly"` | Yes | License type |
|
|
73
|
+
| `customerEmail` | `string` | No | Pre-fill customer email in Stripe |
|
|
74
|
+
| `serverUrl` | `string` | No | License server URL (default: `https://license.sintec.ch`) |
|
|
75
|
+
| `className` | `string` | No | CSS class for styling |
|
|
76
|
+
| `children` | `ReactNode` | No | Button content (default: "Buy Now") |
|
|
77
|
+
| `onCheckoutStart` | `() => void` | No | Called when checkout begins |
|
|
78
|
+
| `onError` | `(error: string) => void` | No | Called on error |
|
|
79
|
+
|
|
80
|
+
### useCheckout Options
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
const { startCheckout, isLoading, error } = useCheckout({
|
|
84
|
+
serverUrl: "https://license.sintec.ch", // optional
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### startCheckout Parameters
|
|
89
|
+
|
|
90
|
+
| Param | Type | Required | Description |
|
|
91
|
+
|-------|------|----------|-------------|
|
|
92
|
+
| `productSlug` | `string` | Yes | Product identifier |
|
|
93
|
+
| `licenseType` | `"perpetual" \| "monthly" \| "yearly"` | Yes | License type |
|
|
94
|
+
| `customerEmail` | `string` | No | Pre-fill customer email |
|
|
95
|
+
|
|
96
|
+
## How It Works
|
|
97
|
+
|
|
98
|
+
1. User clicks the checkout button
|
|
99
|
+
2. Package calls the license server API to create a Stripe checkout session
|
|
100
|
+
3. User is redirected to Stripe to complete payment
|
|
101
|
+
4. After payment, user is redirected to the license server success page
|
|
102
|
+
5. Success page displays the generated license key
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
type LicenseType = "perpetual" | "monthly" | "yearly";
|
|
4
|
+
interface CheckoutOptions {
|
|
5
|
+
/** Base URL of the license server (defaults to https://license.sintec.ch) */
|
|
6
|
+
serverUrl?: string;
|
|
7
|
+
}
|
|
8
|
+
interface StartCheckoutParams {
|
|
9
|
+
/** Product slug identifier */
|
|
10
|
+
productSlug: string;
|
|
11
|
+
/** Type of license to purchase */
|
|
12
|
+
licenseType: LicenseType;
|
|
13
|
+
/** Customer email (optional, pre-fills Stripe checkout) */
|
|
14
|
+
customerEmail?: string;
|
|
15
|
+
}
|
|
16
|
+
interface CheckoutButtonProps extends StartCheckoutParams {
|
|
17
|
+
/** Base URL of the license server */
|
|
18
|
+
serverUrl?: string;
|
|
19
|
+
/** Additional CSS classes */
|
|
20
|
+
className?: string;
|
|
21
|
+
/** Button content */
|
|
22
|
+
children?: React.ReactNode;
|
|
23
|
+
/** Callback when checkout starts */
|
|
24
|
+
onCheckoutStart?: () => void;
|
|
25
|
+
/** Callback on error */
|
|
26
|
+
onError?: (error: string) => void;
|
|
27
|
+
}
|
|
28
|
+
interface CheckoutSessionResponse {
|
|
29
|
+
checkoutUrl: string;
|
|
30
|
+
}
|
|
31
|
+
interface CheckoutErrorResponse {
|
|
32
|
+
error: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
declare function CheckoutButton({ productSlug, licenseType, customerEmail, serverUrl, className, children, onCheckoutStart, onError, }: CheckoutButtonProps): react_jsx_runtime.JSX.Element;
|
|
36
|
+
|
|
37
|
+
interface UseCheckoutReturn {
|
|
38
|
+
/** Start the checkout process */
|
|
39
|
+
startCheckout: (params: StartCheckoutParams) => Promise<void>;
|
|
40
|
+
/** Whether a checkout is currently in progress */
|
|
41
|
+
isLoading: boolean;
|
|
42
|
+
/** Last error message, if any */
|
|
43
|
+
error: string | null;
|
|
44
|
+
}
|
|
45
|
+
declare function useCheckout(options?: CheckoutOptions): UseCheckoutReturn;
|
|
46
|
+
|
|
47
|
+
export { CheckoutButton, type CheckoutButtonProps, type CheckoutErrorResponse, type CheckoutOptions, type CheckoutSessionResponse, type LicenseType, type StartCheckoutParams, type UseCheckoutReturn, useCheckout };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
type LicenseType = "perpetual" | "monthly" | "yearly";
|
|
4
|
+
interface CheckoutOptions {
|
|
5
|
+
/** Base URL of the license server (defaults to https://license.sintec.ch) */
|
|
6
|
+
serverUrl?: string;
|
|
7
|
+
}
|
|
8
|
+
interface StartCheckoutParams {
|
|
9
|
+
/** Product slug identifier */
|
|
10
|
+
productSlug: string;
|
|
11
|
+
/** Type of license to purchase */
|
|
12
|
+
licenseType: LicenseType;
|
|
13
|
+
/** Customer email (optional, pre-fills Stripe checkout) */
|
|
14
|
+
customerEmail?: string;
|
|
15
|
+
}
|
|
16
|
+
interface CheckoutButtonProps extends StartCheckoutParams {
|
|
17
|
+
/** Base URL of the license server */
|
|
18
|
+
serverUrl?: string;
|
|
19
|
+
/** Additional CSS classes */
|
|
20
|
+
className?: string;
|
|
21
|
+
/** Button content */
|
|
22
|
+
children?: React.ReactNode;
|
|
23
|
+
/** Callback when checkout starts */
|
|
24
|
+
onCheckoutStart?: () => void;
|
|
25
|
+
/** Callback on error */
|
|
26
|
+
onError?: (error: string) => void;
|
|
27
|
+
}
|
|
28
|
+
interface CheckoutSessionResponse {
|
|
29
|
+
checkoutUrl: string;
|
|
30
|
+
}
|
|
31
|
+
interface CheckoutErrorResponse {
|
|
32
|
+
error: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
declare function CheckoutButton({ productSlug, licenseType, customerEmail, serverUrl, className, children, onCheckoutStart, onError, }: CheckoutButtonProps): react_jsx_runtime.JSX.Element;
|
|
36
|
+
|
|
37
|
+
interface UseCheckoutReturn {
|
|
38
|
+
/** Start the checkout process */
|
|
39
|
+
startCheckout: (params: StartCheckoutParams) => Promise<void>;
|
|
40
|
+
/** Whether a checkout is currently in progress */
|
|
41
|
+
isLoading: boolean;
|
|
42
|
+
/** Last error message, if any */
|
|
43
|
+
error: string | null;
|
|
44
|
+
}
|
|
45
|
+
declare function useCheckout(options?: CheckoutOptions): UseCheckoutReturn;
|
|
46
|
+
|
|
47
|
+
export { CheckoutButton, type CheckoutButtonProps, type CheckoutErrorResponse, type CheckoutOptions, type CheckoutSessionResponse, type LicenseType, type StartCheckoutParams, type UseCheckoutReturn, useCheckout };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
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
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
CheckoutButton: () => CheckoutButton,
|
|
24
|
+
useCheckout: () => useCheckout
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// src/useCheckout.ts
|
|
29
|
+
var import_react = require("react");
|
|
30
|
+
var DEFAULT_SERVER_URL = "https://license.sintec.ch";
|
|
31
|
+
function useCheckout(options = {}) {
|
|
32
|
+
const { serverUrl = DEFAULT_SERVER_URL } = options;
|
|
33
|
+
const [isLoading, setIsLoading] = (0, import_react.useState)(false);
|
|
34
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
35
|
+
const startCheckout = (0, import_react.useCallback)(
|
|
36
|
+
async (params) => {
|
|
37
|
+
setIsLoading(true);
|
|
38
|
+
setError(null);
|
|
39
|
+
try {
|
|
40
|
+
const response = await fetch(`${serverUrl}/api/v1/checkout/session`, {
|
|
41
|
+
method: "POST",
|
|
42
|
+
headers: {
|
|
43
|
+
"Content-Type": "application/json"
|
|
44
|
+
},
|
|
45
|
+
body: JSON.stringify({
|
|
46
|
+
productSlug: params.productSlug,
|
|
47
|
+
licenseType: params.licenseType,
|
|
48
|
+
customerEmail: params.customerEmail
|
|
49
|
+
})
|
|
50
|
+
});
|
|
51
|
+
const data = await response.json();
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
const errorData = data;
|
|
54
|
+
throw new Error(errorData.error || "Failed to create checkout session");
|
|
55
|
+
}
|
|
56
|
+
const successData = data;
|
|
57
|
+
if (successData.checkoutUrl) {
|
|
58
|
+
window.location.href = successData.checkoutUrl;
|
|
59
|
+
} else {
|
|
60
|
+
throw new Error("No checkout URL returned");
|
|
61
|
+
}
|
|
62
|
+
} catch (err) {
|
|
63
|
+
const message = err instanceof Error ? err.message : "Failed to start checkout";
|
|
64
|
+
setError(message);
|
|
65
|
+
throw err;
|
|
66
|
+
} finally {
|
|
67
|
+
setIsLoading(false);
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
[serverUrl]
|
|
71
|
+
);
|
|
72
|
+
return {
|
|
73
|
+
startCheckout,
|
|
74
|
+
isLoading,
|
|
75
|
+
error
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/CheckoutButton.tsx
|
|
80
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
81
|
+
function CheckoutButton({
|
|
82
|
+
productSlug,
|
|
83
|
+
licenseType,
|
|
84
|
+
customerEmail,
|
|
85
|
+
serverUrl,
|
|
86
|
+
className,
|
|
87
|
+
children = "Buy Now",
|
|
88
|
+
onCheckoutStart,
|
|
89
|
+
onError
|
|
90
|
+
}) {
|
|
91
|
+
const { startCheckout, isLoading } = useCheckout({ serverUrl });
|
|
92
|
+
const handleClick = async () => {
|
|
93
|
+
onCheckoutStart?.();
|
|
94
|
+
try {
|
|
95
|
+
await startCheckout({
|
|
96
|
+
productSlug,
|
|
97
|
+
licenseType,
|
|
98
|
+
customerEmail
|
|
99
|
+
});
|
|
100
|
+
} catch (err) {
|
|
101
|
+
const message = err instanceof Error ? err.message : "Checkout failed";
|
|
102
|
+
onError?.(message);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
106
|
+
"button",
|
|
107
|
+
{
|
|
108
|
+
type: "button",
|
|
109
|
+
onClick: handleClick,
|
|
110
|
+
disabled: isLoading,
|
|
111
|
+
className,
|
|
112
|
+
children: isLoading ? "Loading..." : children
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
117
|
+
0 && (module.exports = {
|
|
118
|
+
CheckoutButton,
|
|
119
|
+
useCheckout
|
|
120
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// src/useCheckout.ts
|
|
2
|
+
import { useState, useCallback } from "react";
|
|
3
|
+
var DEFAULT_SERVER_URL = "https://license.sintec.ch";
|
|
4
|
+
function useCheckout(options = {}) {
|
|
5
|
+
const { serverUrl = DEFAULT_SERVER_URL } = options;
|
|
6
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
7
|
+
const [error, setError] = useState(null);
|
|
8
|
+
const startCheckout = useCallback(
|
|
9
|
+
async (params) => {
|
|
10
|
+
setIsLoading(true);
|
|
11
|
+
setError(null);
|
|
12
|
+
try {
|
|
13
|
+
const response = await fetch(`${serverUrl}/api/v1/checkout/session`, {
|
|
14
|
+
method: "POST",
|
|
15
|
+
headers: {
|
|
16
|
+
"Content-Type": "application/json"
|
|
17
|
+
},
|
|
18
|
+
body: JSON.stringify({
|
|
19
|
+
productSlug: params.productSlug,
|
|
20
|
+
licenseType: params.licenseType,
|
|
21
|
+
customerEmail: params.customerEmail
|
|
22
|
+
})
|
|
23
|
+
});
|
|
24
|
+
const data = await response.json();
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
const errorData = data;
|
|
27
|
+
throw new Error(errorData.error || "Failed to create checkout session");
|
|
28
|
+
}
|
|
29
|
+
const successData = data;
|
|
30
|
+
if (successData.checkoutUrl) {
|
|
31
|
+
window.location.href = successData.checkoutUrl;
|
|
32
|
+
} else {
|
|
33
|
+
throw new Error("No checkout URL returned");
|
|
34
|
+
}
|
|
35
|
+
} catch (err) {
|
|
36
|
+
const message = err instanceof Error ? err.message : "Failed to start checkout";
|
|
37
|
+
setError(message);
|
|
38
|
+
throw err;
|
|
39
|
+
} finally {
|
|
40
|
+
setIsLoading(false);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
[serverUrl]
|
|
44
|
+
);
|
|
45
|
+
return {
|
|
46
|
+
startCheckout,
|
|
47
|
+
isLoading,
|
|
48
|
+
error
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/CheckoutButton.tsx
|
|
53
|
+
import { jsx } from "react/jsx-runtime";
|
|
54
|
+
function CheckoutButton({
|
|
55
|
+
productSlug,
|
|
56
|
+
licenseType,
|
|
57
|
+
customerEmail,
|
|
58
|
+
serverUrl,
|
|
59
|
+
className,
|
|
60
|
+
children = "Buy Now",
|
|
61
|
+
onCheckoutStart,
|
|
62
|
+
onError
|
|
63
|
+
}) {
|
|
64
|
+
const { startCheckout, isLoading } = useCheckout({ serverUrl });
|
|
65
|
+
const handleClick = async () => {
|
|
66
|
+
onCheckoutStart?.();
|
|
67
|
+
try {
|
|
68
|
+
await startCheckout({
|
|
69
|
+
productSlug,
|
|
70
|
+
licenseType,
|
|
71
|
+
customerEmail
|
|
72
|
+
});
|
|
73
|
+
} catch (err) {
|
|
74
|
+
const message = err instanceof Error ? err.message : "Checkout failed";
|
|
75
|
+
onError?.(message);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
return /* @__PURE__ */ jsx(
|
|
79
|
+
"button",
|
|
80
|
+
{
|
|
81
|
+
type: "button",
|
|
82
|
+
onClick: handleClick,
|
|
83
|
+
disabled: isLoading,
|
|
84
|
+
className,
|
|
85
|
+
children: isLoading ? "Loading..." : children
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
export {
|
|
90
|
+
CheckoutButton,
|
|
91
|
+
useCheckout
|
|
92
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sintecinformatik/checkout",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Checkout components for Sintec License Server",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
20
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
21
|
+
"lint": "eslint src/",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"react": ">=18.0.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/react": "^19.0.0",
|
|
29
|
+
"react": "^19.0.0",
|
|
30
|
+
"tsup": "^8.0.0",
|
|
31
|
+
"typescript": "^5.0.0"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"checkout",
|
|
35
|
+
"stripe",
|
|
36
|
+
"license",
|
|
37
|
+
"sintec"
|
|
38
|
+
],
|
|
39
|
+
"author": "Sintec",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "https://github.com/gitmeupchris/SintecLicenseServer.git",
|
|
44
|
+
"directory": "checkout"
|
|
45
|
+
}
|
|
46
|
+
}
|