@libreapps/checkout 2.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/.turbo/turbo-build.log +4 -0
- package/CHANGELOG.md +24 -0
- package/LICENSE.md +21 -0
- package/README.md +211 -0
- package/client.ts +318 -0
- package/dist/client.d.ts +80 -0
- package/dist/client.js +229 -0
- package/dist/client.js.map +1 -0
- package/dist/elements/checkout-form.d.ts +14 -0
- package/dist/elements/checkout-form.js +112 -0
- package/dist/elements/checkout-form.js.map +1 -0
- package/dist/elements/index.d.ts +7 -0
- package/dist/elements/index.js +7 -0
- package/dist/elements/index.js.map +1 -0
- package/dist/embed/index.d.ts +6 -0
- package/dist/embed/index.js +7 -0
- package/dist/embed/index.js.map +1 -0
- package/dist/embed/libreapps-checkout.d.ts +32 -0
- package/dist/embed/libreapps-checkout.js +131 -0
- package/dist/embed/libreapps-checkout.js.map +1 -0
- package/dist/hooks/index.d.ts +5 -0
- package/dist/hooks/index.js +5 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/use-checkout.d.ts +42 -0
- package/dist/hooks/use-checkout.js +168 -0
- package/dist/hooks/use-checkout.js.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +219 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/elements/checkout-form.tsx +543 -0
- package/elements/index.ts +8 -0
- package/embed/index.ts +7 -0
- package/embed/libreapps-checkout.ts +172 -0
- package/hooks/index.ts +6 -0
- package/hooks/use-checkout.tsx +244 -0
- package/index.ts +70 -0
- package/package.json +57 -0
- package/tsconfig.json +15 -0
- package/types.ts +301 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# @libreapps/checkout
|
|
2
|
+
|
|
3
|
+
## 2.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Reverted package scope to @libreapps.
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @libreapps/auth@3.0.1
|
|
10
|
+
- @libreapps/commerce@7.5.1
|
|
11
|
+
- @libreapps/ui@5.4.1
|
|
12
|
+
|
|
13
|
+
## 2.0.0
|
|
14
|
+
|
|
15
|
+
### Minor Changes
|
|
16
|
+
|
|
17
|
+
- 6f41d89: Initial release of LibreApps UI packages to NPM
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- Updated dependencies [6f41d89]
|
|
22
|
+
- @libreapps/ui@5.4.0
|
|
23
|
+
- @libreapps/auth@3.0.0
|
|
24
|
+
- @libreapps/commerce@7.5.0
|
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LibreApps.com
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# @libreapps/checkout
|
|
2
|
+
|
|
3
|
+
Embeddable checkout widget for LibreApps Commerce - a Stripe-like checkout experience.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @libreapps/checkout
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @libreapps/checkout
|
|
11
|
+
# or
|
|
12
|
+
yarn add @libreapps/checkout
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
### React Integration
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { CheckoutProvider, CheckoutForm } from '@libreapps/checkout'
|
|
21
|
+
|
|
22
|
+
function CheckoutPage() {
|
|
23
|
+
return (
|
|
24
|
+
<CheckoutProvider
|
|
25
|
+
options={{
|
|
26
|
+
apiKey: 'pk_live_xxx',
|
|
27
|
+
onSuccess: (result) => {
|
|
28
|
+
console.log('Payment successful!', result.orderId)
|
|
29
|
+
},
|
|
30
|
+
onError: (error) => {
|
|
31
|
+
console.error('Payment failed:', error.message)
|
|
32
|
+
}
|
|
33
|
+
}}
|
|
34
|
+
sessionId="cs_xxx" // Optional: load existing session
|
|
35
|
+
>
|
|
36
|
+
<CheckoutForm
|
|
37
|
+
showSummary={true}
|
|
38
|
+
expressCheckout={true}
|
|
39
|
+
submitText="Complete Purchase"
|
|
40
|
+
/>
|
|
41
|
+
</CheckoutProvider>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Using the Hook
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import { CheckoutProvider, useCheckout } from '@libreapps/checkout'
|
|
50
|
+
|
|
51
|
+
function CustomCheckout() {
|
|
52
|
+
const {
|
|
53
|
+
session,
|
|
54
|
+
step,
|
|
55
|
+
isLoading,
|
|
56
|
+
isProcessing,
|
|
57
|
+
createSession,
|
|
58
|
+
confirmPayment,
|
|
59
|
+
nextStep,
|
|
60
|
+
prevStep
|
|
61
|
+
} = useCheckout()
|
|
62
|
+
|
|
63
|
+
const handleCreateSession = async () => {
|
|
64
|
+
await createSession({
|
|
65
|
+
lineItems: [
|
|
66
|
+
{
|
|
67
|
+
name: 'Premium Plan',
|
|
68
|
+
description: 'Monthly subscription',
|
|
69
|
+
unitPrice: 2999, // $29.99 in cents
|
|
70
|
+
quantity: 1
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
customer: {
|
|
74
|
+
email: 'customer@example.com'
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Build your custom UI...
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Vanilla JavaScript
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { createCheckout } from '@libreapps/checkout'
|
|
87
|
+
|
|
88
|
+
const checkout = createCheckout({
|
|
89
|
+
apiKey: 'pk_live_xxx',
|
|
90
|
+
mode: 'production' // or 'sandbox'
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
// Create a session
|
|
94
|
+
const session = await checkout.createSession({
|
|
95
|
+
lineItems: [
|
|
96
|
+
{
|
|
97
|
+
name: 'Product Name',
|
|
98
|
+
unitPrice: 1999,
|
|
99
|
+
quantity: 1
|
|
100
|
+
}
|
|
101
|
+
],
|
|
102
|
+
successUrl: 'https://example.com/success',
|
|
103
|
+
cancelUrl: 'https://example.com/cancel'
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
// Redirect to hosted checkout
|
|
107
|
+
checkout.redirectToCheckout(session.id)
|
|
108
|
+
|
|
109
|
+
// Or open in a popup
|
|
110
|
+
checkout.openPopup(session.id)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Embed Script (No Build Step)
|
|
114
|
+
|
|
115
|
+
```html
|
|
116
|
+
<!-- Add the script -->
|
|
117
|
+
<script src="https://js.libreapps.com/checkout.js"></script>
|
|
118
|
+
|
|
119
|
+
<!-- Add checkout buttons -->
|
|
120
|
+
<button
|
|
121
|
+
data-libreapps-checkout
|
|
122
|
+
data-libreapps-key="pk_live_xxx"
|
|
123
|
+
data-libreapps-amount="1999"
|
|
124
|
+
data-libreapps-currency="usd"
|
|
125
|
+
data-libreapps-name="Product Name"
|
|
126
|
+
data-libreapps-description="Product description"
|
|
127
|
+
data-libreapps-success-url="https://example.com/success"
|
|
128
|
+
>
|
|
129
|
+
Pay $19.99
|
|
130
|
+
</button>
|
|
131
|
+
|
|
132
|
+
<!-- Initialize (optional, for global config) -->
|
|
133
|
+
<script>
|
|
134
|
+
LibreAppsCheckout.init('pk_live_xxx', {
|
|
135
|
+
mode: 'production'
|
|
136
|
+
})
|
|
137
|
+
</script>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## API Reference
|
|
141
|
+
|
|
142
|
+
### CheckoutOptions
|
|
143
|
+
|
|
144
|
+
| Option | Type | Description |
|
|
145
|
+
|--------|------|-------------|
|
|
146
|
+
| `apiKey` | `string` | Your LibreApps publishable API key (required) |
|
|
147
|
+
| `mode` | `'production' \| 'sandbox'` | Environment mode |
|
|
148
|
+
| `apiEndpoint` | `string` | Custom API endpoint |
|
|
149
|
+
| `locale` | `string` | Locale for i18n |
|
|
150
|
+
| `appearance` | `CheckoutAppearance` | Theme customization |
|
|
151
|
+
| `onSuccess` | `(result) => void` | Success callback |
|
|
152
|
+
| `onError` | `(error) => void` | Error callback |
|
|
153
|
+
| `onCancel` | `() => void` | Cancel callback |
|
|
154
|
+
|
|
155
|
+
### CheckoutAppearance
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
{
|
|
159
|
+
theme: 'light' | 'dark' | 'auto',
|
|
160
|
+
primaryColor: '#0066FF',
|
|
161
|
+
backgroundColor: '#FFFFFF',
|
|
162
|
+
borderRadius: '8px',
|
|
163
|
+
fontFamily: 'Inter, sans-serif',
|
|
164
|
+
variables: {
|
|
165
|
+
'--libreapps-spacing': '16px'
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### CreateSessionParams
|
|
171
|
+
|
|
172
|
+
| Param | Type | Description |
|
|
173
|
+
|-------|------|-------------|
|
|
174
|
+
| `lineItems` | `LineItem[]` | Products to purchase (required) |
|
|
175
|
+
| `customer` | `Customer` | Customer information |
|
|
176
|
+
| `successUrl` | `string` | Redirect URL on success |
|
|
177
|
+
| `cancelUrl` | `string` | Redirect URL on cancel |
|
|
178
|
+
| `currency` | `string` | Currency code (default: 'usd') |
|
|
179
|
+
| `shipping` | `boolean` | Enable shipping collection |
|
|
180
|
+
| `paymentMethods` | `string[]` | Allowed payment methods |
|
|
181
|
+
| `promoCode` | `string` | Pre-applied promo code |
|
|
182
|
+
|
|
183
|
+
## Styling
|
|
184
|
+
|
|
185
|
+
The checkout widget uses CSS custom properties for easy theming:
|
|
186
|
+
|
|
187
|
+
```css
|
|
188
|
+
.libreapps-checkout-form {
|
|
189
|
+
--libreapps-primary: #0066FF;
|
|
190
|
+
--libreapps-primary-hover: #0052CC;
|
|
191
|
+
--libreapps-bg: #FFFFFF;
|
|
192
|
+
--libreapps-bg-secondary: #F5F5F5;
|
|
193
|
+
--libreapps-text: #1A1A1A;
|
|
194
|
+
--libreapps-text-secondary: #666666;
|
|
195
|
+
--libreapps-border: #E5E5E5;
|
|
196
|
+
--libreapps-radius: 8px;
|
|
197
|
+
--libreapps-font: 'Inter', -apple-system, sans-serif;
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Payment Methods
|
|
202
|
+
|
|
203
|
+
- Credit/Debit Cards (Visa, Mastercard, Amex, etc.)
|
|
204
|
+
- Apple Pay
|
|
205
|
+
- Google Pay
|
|
206
|
+
- Cryptocurrency (ETH, USDC, etc.)
|
|
207
|
+
- Bank Transfers
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
BSD-3-Clause
|
package/client.ts
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @libreapps/checkout - Client
|
|
3
|
+
*
|
|
4
|
+
* LibreApps Checkout API client - similar to Stripe.js
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
CheckoutOptions,
|
|
9
|
+
CheckoutSession,
|
|
10
|
+
CreateSessionParams,
|
|
11
|
+
CheckoutResult,
|
|
12
|
+
CheckoutError,
|
|
13
|
+
CheckoutAddress,
|
|
14
|
+
CheckoutPaymentMethod
|
|
15
|
+
} from './types'
|
|
16
|
+
|
|
17
|
+
const DEFAULT_API_ENDPOINT = 'https://api.libreapps.com/v1'
|
|
18
|
+
const SANDBOX_API_ENDPOINT = 'https://sandbox.libreapps.com/v1'
|
|
19
|
+
|
|
20
|
+
export class LibreAppsCheckout {
|
|
21
|
+
private apiKey: string
|
|
22
|
+
private apiEndpoint: string
|
|
23
|
+
private mode: 'production' | 'sandbox'
|
|
24
|
+
private options: CheckoutOptions
|
|
25
|
+
|
|
26
|
+
constructor(options: CheckoutOptions) {
|
|
27
|
+
if (!options.apiKey) {
|
|
28
|
+
throw new Error('LibreApps Checkout: apiKey is required')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
this.apiKey = options.apiKey
|
|
32
|
+
this.mode = options.mode ?? 'production'
|
|
33
|
+
this.apiEndpoint = options.apiEndpoint ??
|
|
34
|
+
(this.mode === 'sandbox' ? SANDBOX_API_ENDPOINT : DEFAULT_API_ENDPOINT)
|
|
35
|
+
this.options = options
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Create a new checkout session
|
|
40
|
+
*/
|
|
41
|
+
async createSession(params: CreateSessionParams): Promise<CheckoutSession> {
|
|
42
|
+
const response = await this.request<CheckoutSession>('POST', '/checkout/sessions', {
|
|
43
|
+
line_items: params.lineItems.map(item => ({
|
|
44
|
+
product_id: item.productId,
|
|
45
|
+
sku: item.sku,
|
|
46
|
+
name: item.name,
|
|
47
|
+
description: item.description,
|
|
48
|
+
image_url: item.imageUrl,
|
|
49
|
+
quantity: item.quantity,
|
|
50
|
+
unit_price: item.unitPrice,
|
|
51
|
+
currency: item.currency ?? 'usd',
|
|
52
|
+
metadata: item.metadata
|
|
53
|
+
})),
|
|
54
|
+
customer: params.customer ? {
|
|
55
|
+
email: params.customer.email,
|
|
56
|
+
name: params.customer.name,
|
|
57
|
+
phone: params.customer.phone
|
|
58
|
+
} : undefined,
|
|
59
|
+
success_url: params.successUrl,
|
|
60
|
+
cancel_url: params.cancelUrl,
|
|
61
|
+
currency: params.currency ?? 'usd',
|
|
62
|
+
metadata: params.metadata,
|
|
63
|
+
shipping: params.shipping ?? true,
|
|
64
|
+
collect_phone: params.collectPhone ?? false,
|
|
65
|
+
payment_methods: params.paymentMethods ?? ['card'],
|
|
66
|
+
promo_code: params.promoCode
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
return this.normalizeSession(response)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Retrieve an existing checkout session
|
|
74
|
+
*/
|
|
75
|
+
async retrieveSession(sessionId: string): Promise<CheckoutSession> {
|
|
76
|
+
const response = await this.request<CheckoutSession>('GET', `/checkout/sessions/${sessionId}`)
|
|
77
|
+
return this.normalizeSession(response)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Update shipping address
|
|
82
|
+
*/
|
|
83
|
+
async updateShipping(sessionId: string, address: CheckoutAddress): Promise<CheckoutSession> {
|
|
84
|
+
const response = await this.request<CheckoutSession>(
|
|
85
|
+
'PATCH',
|
|
86
|
+
`/checkout/sessions/${sessionId}/shipping`,
|
|
87
|
+
{
|
|
88
|
+
line1: address.line1,
|
|
89
|
+
line2: address.line2,
|
|
90
|
+
city: address.city,
|
|
91
|
+
state: address.state,
|
|
92
|
+
postal_code: address.postalCode,
|
|
93
|
+
country: address.country
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
return this.normalizeSession(response)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Update billing address
|
|
101
|
+
*/
|
|
102
|
+
async updateBilling(sessionId: string, address: CheckoutAddress): Promise<CheckoutSession> {
|
|
103
|
+
const response = await this.request<CheckoutSession>(
|
|
104
|
+
'PATCH',
|
|
105
|
+
`/checkout/sessions/${sessionId}/billing`,
|
|
106
|
+
{
|
|
107
|
+
line1: address.line1,
|
|
108
|
+
line2: address.line2,
|
|
109
|
+
city: address.city,
|
|
110
|
+
state: address.state,
|
|
111
|
+
postal_code: address.postalCode,
|
|
112
|
+
country: address.country
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
return this.normalizeSession(response)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Apply a promo code
|
|
120
|
+
*/
|
|
121
|
+
async applyPromoCode(sessionId: string, code: string): Promise<CheckoutSession> {
|
|
122
|
+
const response = await this.request<CheckoutSession>(
|
|
123
|
+
'POST',
|
|
124
|
+
`/checkout/sessions/${sessionId}/promo`,
|
|
125
|
+
{ code }
|
|
126
|
+
)
|
|
127
|
+
return this.normalizeSession(response)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Process payment
|
|
132
|
+
*/
|
|
133
|
+
async confirmPayment(
|
|
134
|
+
sessionId: string,
|
|
135
|
+
paymentMethod: {
|
|
136
|
+
type: 'card'
|
|
137
|
+
card: {
|
|
138
|
+
number: string
|
|
139
|
+
expMonth: number
|
|
140
|
+
expYear: number
|
|
141
|
+
cvc: string
|
|
142
|
+
}
|
|
143
|
+
} | {
|
|
144
|
+
type: 'crypto'
|
|
145
|
+
crypto: {
|
|
146
|
+
currency: string
|
|
147
|
+
network: string
|
|
148
|
+
}
|
|
149
|
+
} | {
|
|
150
|
+
type: 'apple_pay' | 'google_pay'
|
|
151
|
+
token: string
|
|
152
|
+
}
|
|
153
|
+
): Promise<CheckoutResult> {
|
|
154
|
+
const response = await this.request<{
|
|
155
|
+
session_id: string
|
|
156
|
+
order_id: string
|
|
157
|
+
status: 'success' | 'pending'
|
|
158
|
+
payment_intent_id?: string
|
|
159
|
+
customer_id?: string
|
|
160
|
+
receipt_url?: string
|
|
161
|
+
}>('POST', `/checkout/sessions/${sessionId}/confirm`, {
|
|
162
|
+
payment_method: paymentMethod
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
const result: CheckoutResult = {
|
|
166
|
+
sessionId: response.session_id,
|
|
167
|
+
orderId: response.order_id,
|
|
168
|
+
status: response.status,
|
|
169
|
+
paymentIntentId: response.payment_intent_id,
|
|
170
|
+
customerId: response.customer_id,
|
|
171
|
+
receiptUrl: response.receipt_url
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (this.options.onSuccess && result.status === 'success') {
|
|
175
|
+
this.options.onSuccess(result)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return result
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Cancel session
|
|
183
|
+
*/
|
|
184
|
+
async cancelSession(sessionId: string): Promise<void> {
|
|
185
|
+
await this.request('POST', `/checkout/sessions/${sessionId}/cancel`)
|
|
186
|
+
this.options.onCancel?.()
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Redirect to hosted checkout page
|
|
191
|
+
*/
|
|
192
|
+
redirectToCheckout(sessionId: string): void {
|
|
193
|
+
const host = this.mode === 'sandbox' ? 'sandbox.libreapps.com' : 'checkout.libreapps.com'
|
|
194
|
+
window.location.href = `https://${host}/c/${sessionId}`
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Open checkout in a popup
|
|
199
|
+
*/
|
|
200
|
+
openPopup(sessionId: string): Window | null {
|
|
201
|
+
const host = this.mode === 'sandbox' ? 'sandbox.libreapps.com' : 'checkout.libreapps.com'
|
|
202
|
+
const url = `https://${host}/c/${sessionId}?popup=true`
|
|
203
|
+
const width = 450
|
|
204
|
+
const height = 700
|
|
205
|
+
const left = (window.screen.width - width) / 2
|
|
206
|
+
const top = (window.screen.height - height) / 2
|
|
207
|
+
|
|
208
|
+
return window.open(
|
|
209
|
+
url,
|
|
210
|
+
'LibreAppsCheckout',
|
|
211
|
+
`width=${width},height=${height},left=${left},top=${top},toolbar=no,menubar=no,scrollbars=yes,resizable=yes`
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Make API request
|
|
217
|
+
*/
|
|
218
|
+
private async request<T>(
|
|
219
|
+
method: string,
|
|
220
|
+
path: string,
|
|
221
|
+
body?: Record<string, unknown>
|
|
222
|
+
): Promise<T> {
|
|
223
|
+
const url = `${this.apiEndpoint}${path}`
|
|
224
|
+
|
|
225
|
+
const headers: Record<string, string> = {
|
|
226
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
227
|
+
'Content-Type': 'application/json',
|
|
228
|
+
'X-LibreApps-Client': 'checkout-js/1.0.0'
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const response = await fetch(url, {
|
|
232
|
+
method,
|
|
233
|
+
headers,
|
|
234
|
+
body: body ? JSON.stringify(body) : undefined
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
if (!response.ok) {
|
|
238
|
+
const error = await response.json().catch(() => ({}))
|
|
239
|
+
const checkoutError: CheckoutError = {
|
|
240
|
+
code: error.code ?? 'unknown_error',
|
|
241
|
+
message: error.message ?? 'An unexpected error occurred',
|
|
242
|
+
details: error.details
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (this.options.onError) {
|
|
246
|
+
this.options.onError(checkoutError)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
throw checkoutError
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return response.json()
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Normalize API response to CheckoutSession
|
|
257
|
+
*/
|
|
258
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
259
|
+
private normalizeSession(data: any): CheckoutSession {
|
|
260
|
+
return {
|
|
261
|
+
id: data.id as string,
|
|
262
|
+
status: data.status as CheckoutSession['status'],
|
|
263
|
+
lineItems: (data.line_items as Array<Record<string, unknown>> ?? []).map(item => ({
|
|
264
|
+
id: item.id as string,
|
|
265
|
+
productId: item.product_id as string,
|
|
266
|
+
sku: item.sku as string | undefined,
|
|
267
|
+
name: item.name as string,
|
|
268
|
+
description: item.description as string | undefined,
|
|
269
|
+
imageUrl: item.image_url as string | undefined,
|
|
270
|
+
quantity: item.quantity as number,
|
|
271
|
+
unitPrice: item.unit_price as number,
|
|
272
|
+
totalPrice: item.total_price as number,
|
|
273
|
+
currency: item.currency as string,
|
|
274
|
+
metadata: item.metadata as Record<string, string> | undefined
|
|
275
|
+
})),
|
|
276
|
+
subtotal: data.subtotal as number,
|
|
277
|
+
tax: data.tax as number,
|
|
278
|
+
shipping: data.shipping as number,
|
|
279
|
+
total: data.total as number,
|
|
280
|
+
currency: data.currency as string,
|
|
281
|
+
customer: data.customer ? {
|
|
282
|
+
id: (data.customer as Record<string, unknown>).id as string | undefined,
|
|
283
|
+
email: (data.customer as Record<string, unknown>).email as string,
|
|
284
|
+
name: (data.customer as Record<string, unknown>).name as string | undefined,
|
|
285
|
+
phone: (data.customer as Record<string, unknown>).phone as string | undefined
|
|
286
|
+
} : undefined,
|
|
287
|
+
shippingAddress: data.shipping_address ? this.normalizeAddress(data.shipping_address as Record<string, unknown>) : undefined,
|
|
288
|
+
billingAddress: data.billing_address ? this.normalizeAddress(data.billing_address as Record<string, unknown>) : undefined,
|
|
289
|
+
paymentMethod: data.payment_method as CheckoutPaymentMethod | undefined,
|
|
290
|
+
metadata: data.metadata as Record<string, string> | undefined,
|
|
291
|
+
successUrl: data.success_url as string | undefined,
|
|
292
|
+
cancelUrl: data.cancel_url as string | undefined,
|
|
293
|
+
expiresAt: data.expires_at as string | undefined,
|
|
294
|
+
createdAt: data.created_at as string,
|
|
295
|
+
updatedAt: data.updated_at as string
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private normalizeAddress(data: Record<string, unknown>): CheckoutAddress {
|
|
300
|
+
return {
|
|
301
|
+
line1: data.line1 as string,
|
|
302
|
+
line2: data.line2 as string | undefined,
|
|
303
|
+
city: data.city as string,
|
|
304
|
+
state: data.state as string | undefined,
|
|
305
|
+
postalCode: data.postal_code as string,
|
|
306
|
+
country: data.country as string
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Create a new LibreApps Checkout instance
|
|
313
|
+
*/
|
|
314
|
+
export function createCheckout(options: CheckoutOptions): LibreAppsCheckout {
|
|
315
|
+
return new LibreAppsCheckout(options)
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export default LibreAppsCheckout
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @libreapps/checkout - Client
|
|
3
|
+
*
|
|
4
|
+
* LibreApps Checkout API client - similar to Stripe.js
|
|
5
|
+
*/
|
|
6
|
+
import type { CheckoutOptions, CheckoutSession, CreateSessionParams, CheckoutResult, CheckoutAddress } from './types';
|
|
7
|
+
export declare class LibreAppsCheckout {
|
|
8
|
+
private apiKey;
|
|
9
|
+
private apiEndpoint;
|
|
10
|
+
private mode;
|
|
11
|
+
private options;
|
|
12
|
+
constructor(options: CheckoutOptions);
|
|
13
|
+
/**
|
|
14
|
+
* Create a new checkout session
|
|
15
|
+
*/
|
|
16
|
+
createSession(params: CreateSessionParams): Promise<CheckoutSession>;
|
|
17
|
+
/**
|
|
18
|
+
* Retrieve an existing checkout session
|
|
19
|
+
*/
|
|
20
|
+
retrieveSession(sessionId: string): Promise<CheckoutSession>;
|
|
21
|
+
/**
|
|
22
|
+
* Update shipping address
|
|
23
|
+
*/
|
|
24
|
+
updateShipping(sessionId: string, address: CheckoutAddress): Promise<CheckoutSession>;
|
|
25
|
+
/**
|
|
26
|
+
* Update billing address
|
|
27
|
+
*/
|
|
28
|
+
updateBilling(sessionId: string, address: CheckoutAddress): Promise<CheckoutSession>;
|
|
29
|
+
/**
|
|
30
|
+
* Apply a promo code
|
|
31
|
+
*/
|
|
32
|
+
applyPromoCode(sessionId: string, code: string): Promise<CheckoutSession>;
|
|
33
|
+
/**
|
|
34
|
+
* Process payment
|
|
35
|
+
*/
|
|
36
|
+
confirmPayment(sessionId: string, paymentMethod: {
|
|
37
|
+
type: 'card';
|
|
38
|
+
card: {
|
|
39
|
+
number: string;
|
|
40
|
+
expMonth: number;
|
|
41
|
+
expYear: number;
|
|
42
|
+
cvc: string;
|
|
43
|
+
};
|
|
44
|
+
} | {
|
|
45
|
+
type: 'crypto';
|
|
46
|
+
crypto: {
|
|
47
|
+
currency: string;
|
|
48
|
+
network: string;
|
|
49
|
+
};
|
|
50
|
+
} | {
|
|
51
|
+
type: 'apple_pay' | 'google_pay';
|
|
52
|
+
token: string;
|
|
53
|
+
}): Promise<CheckoutResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Cancel session
|
|
56
|
+
*/
|
|
57
|
+
cancelSession(sessionId: string): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Redirect to hosted checkout page
|
|
60
|
+
*/
|
|
61
|
+
redirectToCheckout(sessionId: string): void;
|
|
62
|
+
/**
|
|
63
|
+
* Open checkout in a popup
|
|
64
|
+
*/
|
|
65
|
+
openPopup(sessionId: string): Window | null;
|
|
66
|
+
/**
|
|
67
|
+
* Make API request
|
|
68
|
+
*/
|
|
69
|
+
private request;
|
|
70
|
+
/**
|
|
71
|
+
* Normalize API response to CheckoutSession
|
|
72
|
+
*/
|
|
73
|
+
private normalizeSession;
|
|
74
|
+
private normalizeAddress;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Create a new LibreApps Checkout instance
|
|
78
|
+
*/
|
|
79
|
+
export declare function createCheckout(options: CheckoutOptions): LibreAppsCheckout;
|
|
80
|
+
export default LibreAppsCheckout;
|