@flopay/node 0.1.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 +157 -0
- package/dist/index.cjs +260 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +80 -0
- package/dist/index.d.ts +80 -0
- package/dist/index.mjs +223 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# @flopay/node
|
|
2
|
+
|
|
3
|
+
Server-side FloPay SDK for Node.js. Creates checkout sessions via the billing API, manages Stripe customers, and verifies webhooks.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @flopay/node
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Depends on `stripe` (^17.0.0) and `@flopay/shared`.
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
### Initialize
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { FloPay } from '@flopay/node';
|
|
19
|
+
|
|
20
|
+
const flopay = new FloPay('sk_test_...');
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Create a Checkout Session (Billing API)
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
const result = await flopay.checkout.sessions.create({
|
|
27
|
+
billingApiUrl: 'https://billing.example.com',
|
|
28
|
+
checkoutBaseUrl: 'https://checkout.example.com',
|
|
29
|
+
clientId: 'client_123',
|
|
30
|
+
items: [{
|
|
31
|
+
providerItemId: 'prod_abc',
|
|
32
|
+
providerItemName: 'Pro Plan',
|
|
33
|
+
totalAmount: 49.99,
|
|
34
|
+
overrideAmount: 24.99,
|
|
35
|
+
currency: 'USD',
|
|
36
|
+
}],
|
|
37
|
+
subscriptions: [{
|
|
38
|
+
providerPlanId: 'plan_xyz',
|
|
39
|
+
providerPlanName: 'Monthly Plan',
|
|
40
|
+
totalAmount: 9.99,
|
|
41
|
+
currency: 'USD',
|
|
42
|
+
}],
|
|
43
|
+
account: {
|
|
44
|
+
userId: 'user_1',
|
|
45
|
+
email: 'user@example.com',
|
|
46
|
+
firstName: 'John',
|
|
47
|
+
lastName: 'Doe',
|
|
48
|
+
},
|
|
49
|
+
successUrl: 'https://example.com/success',
|
|
50
|
+
cancelUrl: 'https://example.com/cancel',
|
|
51
|
+
checkoutMode: 'confirm', // 'confirm' | 'auto' | 'full'
|
|
52
|
+
couponCodes: ['SAVE10'],
|
|
53
|
+
tagsData: { googleContainerId: 'GTM-XXXX' },
|
|
54
|
+
redirectParams: { email: 'user@example.com', mode: 'confirm' },
|
|
55
|
+
timeoutMs: 12000,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// result = { status: 201, redirectUrl: 'https://checkout.example.com/secure?id=uuid&...' }
|
|
59
|
+
// result = { status: 204 } (payment method already on file)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Retrieve / Expire a Checkout Session (Stripe Direct)
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
const session = await flopay.checkout.sessions.retrieve('cs_test_...');
|
|
66
|
+
// { id, clientSecret, mode, status, amount, currency, metadata }
|
|
67
|
+
|
|
68
|
+
const expired = await flopay.checkout.sessions.expire('cs_test_...');
|
|
69
|
+
|
|
70
|
+
const lineItems = await flopay.checkout.sessions.listLineItems('cs_test_...');
|
|
71
|
+
// [{ price: 'price_xxx', quantity: 1 }]
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Manage Customers
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
const customer = await flopay.customers.create({
|
|
78
|
+
email: 'user@example.com',
|
|
79
|
+
name: 'John Doe',
|
|
80
|
+
metadata: { plan: 'pro' },
|
|
81
|
+
});
|
|
82
|
+
// { id: 'cus_xxx', email: '...', firstName: 'John', lastName: 'Doe' }
|
|
83
|
+
|
|
84
|
+
const retrieved = await flopay.customers.retrieve('cus_xxx');
|
|
85
|
+
|
|
86
|
+
const updated = await flopay.customers.update('cus_xxx', {
|
|
87
|
+
name: 'Jane Doe',
|
|
88
|
+
metadata: { plan: 'enterprise' },
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Verify Webhooks
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { FloPay } from '@flopay/node';
|
|
96
|
+
|
|
97
|
+
const flopay = new FloPay('sk_test_...');
|
|
98
|
+
|
|
99
|
+
// In your webhook handler (e.g. Express, Next.js API route):
|
|
100
|
+
const event = flopay.webhooks.constructEvent(
|
|
101
|
+
requestBody, // raw body string or Buffer
|
|
102
|
+
signatureHeader, // Stripe-Signature header
|
|
103
|
+
'whsec_...', // webhook signing secret
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
console.log(event.type); // e.g. 'checkout.session.completed'
|
|
107
|
+
console.log(event.data); // event payload
|
|
108
|
+
console.log(event.created); // unix timestamp
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## API Reference
|
|
112
|
+
|
|
113
|
+
### Constructor
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
new FloPay(secretKey: string, options?: FloPayNodeOptions)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
| Parameter | Type | Description |
|
|
120
|
+
|-----------|------|-------------|
|
|
121
|
+
| `secretKey` | `string` | Stripe secret key (`sk_test_...` or `sk_live_...`). Required. |
|
|
122
|
+
| `options.apiVersion` | `string?` | API version override |
|
|
123
|
+
| `options.stripeSecretKey` | `string?` | Explicit Stripe key (for future multi-provider support) |
|
|
124
|
+
|
|
125
|
+
### `flopay.checkout.sessions`
|
|
126
|
+
|
|
127
|
+
| Method | Returns | Description |
|
|
128
|
+
|--------|---------|-------------|
|
|
129
|
+
| `create(params)` | `Promise<CheckoutSessionResult>` | Creates a session via billing API (`POST /v1/checkouts/sessions`). Returns `{ status: 201, redirectUrl }` or `{ status: 204 }`. |
|
|
130
|
+
| `retrieve(id)` | `Promise<CheckoutSession>` | Retrieves a Stripe checkout session and normalizes it |
|
|
131
|
+
| `expire(id)` | `Promise<CheckoutSession>` | Expires a Stripe checkout session |
|
|
132
|
+
| `listLineItems(id)` | `Promise<LineItem[]>` | Lists line items for a Stripe checkout session |
|
|
133
|
+
|
|
134
|
+
### `flopay.customers`
|
|
135
|
+
|
|
136
|
+
| Method | Returns | Description |
|
|
137
|
+
|--------|---------|-------------|
|
|
138
|
+
| `create(params)` | `Promise<Customer>` | Creates a Stripe customer |
|
|
139
|
+
| `retrieve(id)` | `Promise<Customer>` | Retrieves a Stripe customer. Throws if deleted. |
|
|
140
|
+
| `update(id, params)` | `Promise<Customer>` | Updates a Stripe customer |
|
|
141
|
+
|
|
142
|
+
### `flopay.webhooks`
|
|
143
|
+
|
|
144
|
+
| Method | Returns | Description |
|
|
145
|
+
|--------|---------|-------------|
|
|
146
|
+
| `constructEvent(payload, signature, secret)` | `WebhookEvent` | Verifies and parses a Stripe webhook event |
|
|
147
|
+
|
|
148
|
+
### Types
|
|
149
|
+
|
|
150
|
+
| Type | Description |
|
|
151
|
+
|------|-------------|
|
|
152
|
+
| `FloPayNodeOptions` | Constructor options |
|
|
153
|
+
| `CreateSessionParams` | Full session creation parameters (from `@flopay/shared`) |
|
|
154
|
+
| `CheckoutSessionResult` | `{ status: 201, redirectUrl }` / `{ status: 204 }` / `{ status: number }` |
|
|
155
|
+
| `CheckoutSession` | Normalized session: `id`, `clientSecret`, `mode`, `status`, `amount`, `currency`, `metadata` |
|
|
156
|
+
| `Customer` | `id`, `email`, `firstName?`, `lastName?` |
|
|
157
|
+
| `WebhookEvent` | `id`, `type`, `data`, `created` |
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
FloPay: () => FloPay
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/flopay-node.ts
|
|
38
|
+
var import_stripe = __toESM(require("stripe"), 1);
|
|
39
|
+
var import_shared = require("@flopay/shared");
|
|
40
|
+
var FloPay = class {
|
|
41
|
+
constructor(secretKey, options) {
|
|
42
|
+
if (!secretKey) {
|
|
43
|
+
throw new import_shared.FloPayError(
|
|
44
|
+
"A secret key is required to initialize the FloPay server SDK.",
|
|
45
|
+
"authentication_error"
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
this.stripe = new import_stripe.default(secretKey, {
|
|
49
|
+
apiVersion: "2025-04-30.basil",
|
|
50
|
+
appInfo: {
|
|
51
|
+
name: "flopay-node",
|
|
52
|
+
version: import_shared.SDK_VERSION
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
this.checkout = {
|
|
56
|
+
sessions: {
|
|
57
|
+
create: this.createSession.bind(this),
|
|
58
|
+
retrieve: this.retrieveSession.bind(this),
|
|
59
|
+
expire: this.expireSession.bind(this),
|
|
60
|
+
listLineItems: this.listLineItems.bind(this)
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
this.webhooks = {
|
|
64
|
+
constructEvent: this.constructWebhookEvent.bind(this)
|
|
65
|
+
};
|
|
66
|
+
this.customers = {
|
|
67
|
+
create: this.createCustomer.bind(this),
|
|
68
|
+
retrieve: this.retrieveCustomer.bind(this),
|
|
69
|
+
update: this.updateCustomer.bind(this)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Create a checkout session via the billing API.
|
|
74
|
+
*
|
|
75
|
+
* Mirrors `createCheckoutSession` from `clicktech-core-ui/modules`.
|
|
76
|
+
* Posts to `{billingApiUrl}/v1/checkouts/sessions` and returns
|
|
77
|
+
* the session UUID for redirect.
|
|
78
|
+
*/
|
|
79
|
+
async createSession(params) {
|
|
80
|
+
const {
|
|
81
|
+
billingApiUrl,
|
|
82
|
+
checkoutBaseUrl,
|
|
83
|
+
items = [],
|
|
84
|
+
subscriptions = [],
|
|
85
|
+
account,
|
|
86
|
+
successUrl,
|
|
87
|
+
cancelUrl,
|
|
88
|
+
checkoutMode = "confirm",
|
|
89
|
+
couponCodes = [],
|
|
90
|
+
tagsData,
|
|
91
|
+
redirectParams = {},
|
|
92
|
+
timeoutMs = 12e3,
|
|
93
|
+
clientId,
|
|
94
|
+
utmMetadata
|
|
95
|
+
} = params;
|
|
96
|
+
const payload = {
|
|
97
|
+
clientId,
|
|
98
|
+
successUrl,
|
|
99
|
+
cancelUrl,
|
|
100
|
+
checkoutMode,
|
|
101
|
+
items: items.map((item) => ({
|
|
102
|
+
providerItemId: item.providerItemId,
|
|
103
|
+
providerItemName: item.providerItemName ?? null,
|
|
104
|
+
quantity: item.quantity ?? 1,
|
|
105
|
+
totalAmount: item.totalAmount,
|
|
106
|
+
overrideAmount: item.overrideAmount ?? null,
|
|
107
|
+
currency: item.currency ?? "USD"
|
|
108
|
+
})),
|
|
109
|
+
subscriptions: subscriptions.map((sub) => ({
|
|
110
|
+
providerPlanId: sub.providerPlanId,
|
|
111
|
+
providerPlanName: sub.providerPlanName ?? null,
|
|
112
|
+
quantity: sub.quantity ?? 1,
|
|
113
|
+
totalAmount: sub.totalAmount,
|
|
114
|
+
overrideAmount: sub.overrideAmount ?? null,
|
|
115
|
+
currency: sub.currency ?? "USD"
|
|
116
|
+
})),
|
|
117
|
+
accountData: {
|
|
118
|
+
userId: account.userId,
|
|
119
|
+
firstName: account.firstName ?? null,
|
|
120
|
+
lastName: account.lastName ?? null,
|
|
121
|
+
email: account.email,
|
|
122
|
+
country: account.country ?? null,
|
|
123
|
+
gender: account.gender ?? null,
|
|
124
|
+
city: account.city ?? null,
|
|
125
|
+
state: account.state ?? null,
|
|
126
|
+
zip: account.zip ?? null
|
|
127
|
+
},
|
|
128
|
+
couponCodes
|
|
129
|
+
};
|
|
130
|
+
if (tagsData) {
|
|
131
|
+
payload["tagsData"] = tagsData;
|
|
132
|
+
}
|
|
133
|
+
if (utmMetadata?.length) {
|
|
134
|
+
payload["utmMetadata"] = utmMetadata;
|
|
135
|
+
}
|
|
136
|
+
const url = `${billingApiUrl.replace(/\/+$/, "")}/v1/checkouts/sessions`;
|
|
137
|
+
const controller = new AbortController();
|
|
138
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
139
|
+
let status;
|
|
140
|
+
let body;
|
|
141
|
+
try {
|
|
142
|
+
const response = await fetch(url, {
|
|
143
|
+
method: "POST",
|
|
144
|
+
headers: { "Content-Type": "application/json" },
|
|
145
|
+
body: JSON.stringify(payload),
|
|
146
|
+
signal: controller.signal
|
|
147
|
+
});
|
|
148
|
+
status = response.status;
|
|
149
|
+
try {
|
|
150
|
+
body = await response.json();
|
|
151
|
+
} catch {
|
|
152
|
+
}
|
|
153
|
+
} finally {
|
|
154
|
+
clearTimeout(timer);
|
|
155
|
+
}
|
|
156
|
+
if (status === 201) {
|
|
157
|
+
const uuid = body?.data?.uuid;
|
|
158
|
+
if (!uuid) {
|
|
159
|
+
throw new Error("Checkout session created but no UUID was returned by the billing API");
|
|
160
|
+
}
|
|
161
|
+
const redirectUrl = new URL(`${checkoutBaseUrl.replace(/\/+$/, "")}/secure`);
|
|
162
|
+
redirectUrl.searchParams.set("id", uuid);
|
|
163
|
+
for (const [key, value] of Object.entries(redirectParams)) {
|
|
164
|
+
redirectUrl.searchParams.set(key, value);
|
|
165
|
+
}
|
|
166
|
+
return { status: 201, redirectUrl: redirectUrl.toString() };
|
|
167
|
+
}
|
|
168
|
+
if (status === 204) {
|
|
169
|
+
return { status: 204 };
|
|
170
|
+
}
|
|
171
|
+
return { status };
|
|
172
|
+
}
|
|
173
|
+
normalizeStripeSession(session) {
|
|
174
|
+
const statusMap = {
|
|
175
|
+
open: "open",
|
|
176
|
+
complete: "complete",
|
|
177
|
+
expired: "expired"
|
|
178
|
+
};
|
|
179
|
+
return {
|
|
180
|
+
id: session.id,
|
|
181
|
+
clientSecret: session.client_secret ?? "",
|
|
182
|
+
mode: session.mode,
|
|
183
|
+
status: statusMap[session.status ?? "open"] ?? "open",
|
|
184
|
+
amount: session.amount_total ?? 0,
|
|
185
|
+
currency: session.currency ?? "usd",
|
|
186
|
+
metadata: session.metadata ?? {}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
async retrieveSession(id) {
|
|
190
|
+
const session = await this.stripe.checkout.sessions.retrieve(id);
|
|
191
|
+
return this.normalizeStripeSession(session);
|
|
192
|
+
}
|
|
193
|
+
async expireSession(id) {
|
|
194
|
+
const session = await this.stripe.checkout.sessions.expire(id);
|
|
195
|
+
return this.normalizeStripeSession(session);
|
|
196
|
+
}
|
|
197
|
+
async listLineItems(id) {
|
|
198
|
+
const items = await this.stripe.checkout.sessions.listLineItems(id);
|
|
199
|
+
return items.data.map((item) => ({
|
|
200
|
+
price: item.price?.id,
|
|
201
|
+
quantity: item.quantity ?? 1
|
|
202
|
+
}));
|
|
203
|
+
}
|
|
204
|
+
constructWebhookEvent(payload, signature, secret) {
|
|
205
|
+
const event = this.stripe.webhooks.constructEvent(payload, signature, secret);
|
|
206
|
+
return {
|
|
207
|
+
id: event.id,
|
|
208
|
+
type: event.type,
|
|
209
|
+
data: event.data,
|
|
210
|
+
created: event.created
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
async createCustomer(params) {
|
|
214
|
+
const customer = await this.stripe.customers.create({
|
|
215
|
+
email: params.email,
|
|
216
|
+
name: params.name,
|
|
217
|
+
metadata: params.metadata
|
|
218
|
+
});
|
|
219
|
+
return {
|
|
220
|
+
id: customer.id,
|
|
221
|
+
email: customer.email ?? params.email,
|
|
222
|
+
firstName: params.name?.split(" ")[0],
|
|
223
|
+
lastName: params.name?.split(" ").slice(1).join(" ")
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
async retrieveCustomer(id) {
|
|
227
|
+
const customer = await this.stripe.customers.retrieve(id);
|
|
228
|
+
if (customer.deleted) {
|
|
229
|
+
throw new import_shared.FloPayError(
|
|
230
|
+
`Customer ${id} has been deleted`,
|
|
231
|
+
"api_error",
|
|
232
|
+
{ code: "resource_missing" }
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
id: customer.id,
|
|
237
|
+
email: customer.email ?? "",
|
|
238
|
+
firstName: customer.name?.split(" ")[0],
|
|
239
|
+
lastName: customer.name?.split(" ").slice(1).join(" ")
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
async updateCustomer(id, params) {
|
|
243
|
+
const customer = await this.stripe.customers.update(id, {
|
|
244
|
+
email: params.email,
|
|
245
|
+
name: params.name,
|
|
246
|
+
metadata: params.metadata
|
|
247
|
+
});
|
|
248
|
+
return {
|
|
249
|
+
id: customer.id,
|
|
250
|
+
email: customer.email ?? "",
|
|
251
|
+
firstName: customer.name?.split(" ")[0],
|
|
252
|
+
lastName: customer.name?.split(" ").slice(1).join(" ")
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
257
|
+
0 && (module.exports = {
|
|
258
|
+
FloPay
|
|
259
|
+
});
|
|
260
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/flopay-node.ts"],"sourcesContent":["export { FloPay } from './flopay-node.js';\nexport type { FloPayNodeOptions } from './flopay-node.js';\n","import Stripe from 'stripe';\nimport type {\n CheckoutSession,\n LineItem,\n CreateSessionParams,\n CheckoutSessionResult,\n CreateCustomerParams,\n UpdateCustomerParams,\n WebhookEvent,\n Customer,\n} from '@flopay/shared';\nimport { FloPayError, SDK_VERSION } from '@flopay/shared';\n\n/** Options for the FloPay Node SDK constructor. */\nexport interface FloPayNodeOptions {\n apiVersion?: string;\n /** Stripe secret key, required when using Stripe-specific operations. */\n stripeSecretKey?: string;\n}\n\n/**\n * Server-side FloPay SDK.\n *\n * Supports two modes of operation:\n *\n * 1. **Billing API mode** — Creates checkout sessions via the FloPay/ClickTech\n * billing API (`POST /v1/checkouts/sessions`). This mirrors the\n * `createCheckoutSession` function from `clicktech-core-ui`.\n *\n * 2. **Stripe direct mode** — When a `stripeSecretKey` is provided, also\n * exposes direct Stripe operations (customers, webhooks).\n *\n * @example\n * ```ts\n * import { FloPay } from '@flopay/node';\n *\n * const flopay = new FloPay('sk_test_...');\n *\n * // Create session via billing API\n * const result = await flopay.checkout.sessions.create({\n * billingApiUrl: 'https://billing.example.com',\n * checkoutBaseUrl: 'https://checkout.example.com',\n * clientId: 'client_123',\n * items: [{ providerItemId: 'prod_abc', totalAmount: 49.99 }],\n * account: { userId: 'user_1', email: 'user@example.com' },\n * successUrl: '/success',\n * cancelUrl: '/cancel',\n * });\n * ```\n */\nexport class FloPay {\n private readonly stripe: Stripe;\n\n readonly checkout: {\n sessions: {\n /** Create a checkout session via the billing API. */\n create: (params: CreateSessionParams) => Promise<CheckoutSessionResult>;\n /** Retrieve a checkout session from Stripe. */\n retrieve: (id: string) => Promise<CheckoutSession>;\n /** Expire a checkout session on Stripe. */\n expire: (id: string) => Promise<CheckoutSession>;\n /** List line items for a Stripe checkout session. */\n listLineItems: (id: string) => Promise<LineItem[]>;\n };\n };\n\n readonly webhooks: {\n constructEvent: (\n payload: string | Buffer,\n signature: string,\n secret: string,\n ) => WebhookEvent;\n };\n\n readonly customers: {\n create: (params: CreateCustomerParams) => Promise<Customer>;\n retrieve: (id: string) => Promise<Customer>;\n update: (id: string, params: UpdateCustomerParams) => Promise<Customer>;\n };\n\n constructor(secretKey: string, options?: FloPayNodeOptions) {\n if (!secretKey) {\n throw new FloPayError(\n 'A secret key is required to initialize the FloPay server SDK.',\n 'authentication_error',\n );\n }\n\n this.stripe = new Stripe(secretKey, {\n apiVersion: '2025-04-30.basil' as Stripe.LatestApiVersion,\n appInfo: {\n name: 'flopay-node',\n version: SDK_VERSION,\n },\n });\n\n this.checkout = {\n sessions: {\n create: this.createSession.bind(this),\n retrieve: this.retrieveSession.bind(this),\n expire: this.expireSession.bind(this),\n listLineItems: this.listLineItems.bind(this),\n },\n };\n\n this.webhooks = {\n constructEvent: this.constructWebhookEvent.bind(this),\n };\n\n this.customers = {\n create: this.createCustomer.bind(this),\n retrieve: this.retrieveCustomer.bind(this),\n update: this.updateCustomer.bind(this),\n };\n }\n\n /**\n * Create a checkout session via the billing API.\n *\n * Mirrors `createCheckoutSession` from `clicktech-core-ui/modules`.\n * Posts to `{billingApiUrl}/v1/checkouts/sessions` and returns\n * the session UUID for redirect.\n */\n private async createSession(\n params: CreateSessionParams,\n ): Promise<CheckoutSessionResult> {\n const {\n billingApiUrl,\n checkoutBaseUrl,\n items = [],\n subscriptions = [],\n account,\n successUrl,\n cancelUrl,\n checkoutMode = 'confirm',\n couponCodes = [],\n tagsData,\n redirectParams = {},\n timeoutMs = 12000,\n clientId,\n utmMetadata,\n } = params;\n\n const payload: Record<string, unknown> = {\n clientId,\n successUrl,\n cancelUrl,\n checkoutMode,\n items: items.map((item) => ({\n providerItemId: item.providerItemId,\n providerItemName: item.providerItemName ?? null,\n quantity: item.quantity ?? 1,\n totalAmount: item.totalAmount,\n overrideAmount: item.overrideAmount ?? null,\n currency: item.currency ?? 'USD',\n })),\n subscriptions: subscriptions.map((sub) => ({\n providerPlanId: sub.providerPlanId,\n providerPlanName: sub.providerPlanName ?? null,\n quantity: sub.quantity ?? 1,\n totalAmount: sub.totalAmount,\n overrideAmount: sub.overrideAmount ?? null,\n currency: sub.currency ?? 'USD',\n })),\n accountData: {\n userId: account.userId,\n firstName: account.firstName ?? null,\n lastName: account.lastName ?? null,\n email: account.email,\n country: account.country ?? null,\n gender: account.gender ?? null,\n city: account.city ?? null,\n state: account.state ?? null,\n zip: account.zip ?? null,\n },\n couponCodes,\n };\n\n if (tagsData) {\n payload['tagsData'] = tagsData;\n }\n\n if (utmMetadata?.length) {\n payload['utmMetadata'] = utmMetadata;\n }\n\n const url = `${billingApiUrl.replace(/\\/+$/, '')}/v1/checkouts/sessions`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let status: number;\n let body: { data?: { uuid?: string } } | undefined;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n\n status = response.status;\n\n try {\n body = await response.json() as { data?: { uuid?: string } };\n } catch {\n // 204 or empty body\n }\n } finally {\n clearTimeout(timer);\n }\n\n if (status === 201) {\n const uuid = body?.data?.uuid;\n\n if (!uuid) {\n throw new Error('Checkout session created but no UUID was returned by the billing API');\n }\n\n const redirectUrl = new URL(`${checkoutBaseUrl.replace(/\\/+$/, '')}/secure`);\n redirectUrl.searchParams.set('id', uuid);\n\n for (const [key, value] of Object.entries(redirectParams)) {\n redirectUrl.searchParams.set(key, value);\n }\n\n return { status: 201, redirectUrl: redirectUrl.toString() };\n }\n\n if (status === 204) {\n return { status: 204 };\n }\n\n return { status };\n }\n\n private normalizeStripeSession(\n session: Stripe.Checkout.Session,\n ): CheckoutSession {\n const statusMap: Record<string, CheckoutSession['status']> = {\n open: 'open',\n complete: 'complete',\n expired: 'expired',\n };\n\n return {\n id: session.id,\n clientSecret: session.client_secret ?? '',\n mode: session.mode as CheckoutSession['mode'],\n status: statusMap[session.status ?? 'open'] ?? 'open',\n amount: session.amount_total ?? 0,\n currency: session.currency ?? 'usd',\n metadata: (session.metadata as Record<string, string>) ?? {},\n };\n }\n\n private async retrieveSession(id: string): Promise<CheckoutSession> {\n const session = await this.stripe.checkout.sessions.retrieve(id);\n return this.normalizeStripeSession(session);\n }\n\n private async expireSession(id: string): Promise<CheckoutSession> {\n const session = await this.stripe.checkout.sessions.expire(id);\n return this.normalizeStripeSession(session);\n }\n\n private async listLineItems(id: string): Promise<LineItem[]> {\n const items = await this.stripe.checkout.sessions.listLineItems(id);\n return items.data.map((item) => ({\n price: item.price?.id,\n quantity: item.quantity ?? 1,\n }));\n }\n\n private constructWebhookEvent(\n payload: string | Buffer,\n signature: string,\n secret: string,\n ): WebhookEvent {\n const event = this.stripe.webhooks.constructEvent(payload, signature, secret);\n\n return {\n id: event.id,\n type: event.type,\n data: event.data as unknown as Record<string, unknown>,\n created: event.created,\n };\n }\n\n private async createCustomer(params: CreateCustomerParams): Promise<Customer> {\n const customer = await this.stripe.customers.create({\n email: params.email,\n name: params.name,\n metadata: params.metadata,\n });\n\n return {\n id: customer.id,\n email: customer.email ?? params.email,\n firstName: params.name?.split(' ')[0],\n lastName: params.name?.split(' ').slice(1).join(' '),\n };\n }\n\n private async retrieveCustomer(id: string): Promise<Customer> {\n const customer = await this.stripe.customers.retrieve(id);\n\n if (customer.deleted) {\n throw new FloPayError(\n `Customer ${id} has been deleted`,\n 'api_error',\n { code: 'resource_missing' },\n );\n }\n\n return {\n id: customer.id,\n email: customer.email ?? '',\n firstName: customer.name?.split(' ')[0],\n lastName: customer.name?.split(' ').slice(1).join(' '),\n };\n }\n\n private async updateCustomer(\n id: string,\n params: UpdateCustomerParams,\n ): Promise<Customer> {\n const customer = await this.stripe.customers.update(id, {\n email: params.email,\n name: params.name,\n metadata: params.metadata,\n });\n\n return {\n id: customer.id,\n email: customer.email ?? '',\n firstName: customer.name?.split(' ')[0],\n lastName: customer.name?.split(' ').slice(1).join(' '),\n };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAAmB;AAWnB,oBAAyC;AAuClC,IAAM,SAAN,MAAa;AAAA,EA8BlB,YAAY,WAAmB,SAA6B;AAC1D,QAAI,CAAC,WAAW;AACd,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,SAAK,SAAS,IAAI,cAAAA,QAAO,WAAW;AAAA,MAClC,YAAY;AAAA,MACZ,SAAS;AAAA,QACP,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA,IACF,CAAC;AAED,SAAK,WAAW;AAAA,MACd,UAAU;AAAA,QACR,QAAQ,KAAK,cAAc,KAAK,IAAI;AAAA,QACpC,UAAU,KAAK,gBAAgB,KAAK,IAAI;AAAA,QACxC,QAAQ,KAAK,cAAc,KAAK,IAAI;AAAA,QACpC,eAAe,KAAK,cAAc,KAAK,IAAI;AAAA,MAC7C;AAAA,IACF;AAEA,SAAK,WAAW;AAAA,MACd,gBAAgB,KAAK,sBAAsB,KAAK,IAAI;AAAA,IACtD;AAEA,SAAK,YAAY;AAAA,MACf,QAAQ,KAAK,eAAe,KAAK,IAAI;AAAA,MACrC,UAAU,KAAK,iBAAiB,KAAK,IAAI;AAAA,MACzC,QAAQ,KAAK,eAAe,KAAK,IAAI;AAAA,IACvC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,cACZ,QACgC;AAChC,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA,QAAQ,CAAC;AAAA,MACT,gBAAgB,CAAC;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA,eAAe;AAAA,MACf,cAAc,CAAC;AAAA,MACf;AAAA,MACA,iBAAiB,CAAC;AAAA,MAClB,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,IACF,IAAI;AAEJ,UAAM,UAAmC;AAAA,MACvC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,MAAM,IAAI,CAAC,UAAU;AAAA,QAC1B,gBAAgB,KAAK;AAAA,QACrB,kBAAkB,KAAK,oBAAoB;AAAA,QAC3C,UAAU,KAAK,YAAY;AAAA,QAC3B,aAAa,KAAK;AAAA,QAClB,gBAAgB,KAAK,kBAAkB;AAAA,QACvC,UAAU,KAAK,YAAY;AAAA,MAC7B,EAAE;AAAA,MACF,eAAe,cAAc,IAAI,CAAC,SAAS;AAAA,QACzC,gBAAgB,IAAI;AAAA,QACpB,kBAAkB,IAAI,oBAAoB;AAAA,QAC1C,UAAU,IAAI,YAAY;AAAA,QAC1B,aAAa,IAAI;AAAA,QACjB,gBAAgB,IAAI,kBAAkB;AAAA,QACtC,UAAU,IAAI,YAAY;AAAA,MAC5B,EAAE;AAAA,MACF,aAAa;AAAA,QACX,QAAQ,QAAQ;AAAA,QAChB,WAAW,QAAQ,aAAa;AAAA,QAChC,UAAU,QAAQ,YAAY;AAAA,QAC9B,OAAO,QAAQ;AAAA,QACf,SAAS,QAAQ,WAAW;AAAA,QAC5B,QAAQ,QAAQ,UAAU;AAAA,QAC1B,MAAM,QAAQ,QAAQ;AAAA,QACtB,OAAO,QAAQ,SAAS;AAAA,QACxB,KAAK,QAAQ,OAAO;AAAA,MACtB;AAAA,MACA;AAAA,IACF;AAEA,QAAI,UAAU;AACZ,cAAQ,UAAU,IAAI;AAAA,IACxB;AAEA,QAAI,aAAa,QAAQ;AACvB,cAAQ,aAAa,IAAI;AAAA,IAC3B;AAEA,UAAM,MAAM,GAAG,cAAc,QAAQ,QAAQ,EAAE,CAAC;AAChD,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,SAAS;AAE5D,QAAI;AACJ,QAAI;AAEJ,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B,QAAQ,WAAW;AAAA,MACrB,CAAC;AAED,eAAS,SAAS;AAElB,UAAI;AACF,eAAO,MAAM,SAAS,KAAK;AAAA,MAC7B,QAAQ;AAAA,MAER;AAAA,IACF,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAEA,QAAI,WAAW,KAAK;AAClB,YAAM,OAAO,MAAM,MAAM;AAEzB,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,MAAM,sEAAsE;AAAA,MACxF;AAEA,YAAM,cAAc,IAAI,IAAI,GAAG,gBAAgB,QAAQ,QAAQ,EAAE,CAAC,SAAS;AAC3E,kBAAY,aAAa,IAAI,MAAM,IAAI;AAEvC,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,cAAc,GAAG;AACzD,oBAAY,aAAa,IAAI,KAAK,KAAK;AAAA,MACzC;AAEA,aAAO,EAAE,QAAQ,KAAK,aAAa,YAAY,SAAS,EAAE;AAAA,IAC5D;AAEA,QAAI,WAAW,KAAK;AAClB,aAAO,EAAE,QAAQ,IAAI;AAAA,IACvB;AAEA,WAAO,EAAE,OAAO;AAAA,EAClB;AAAA,EAEQ,uBACN,SACiB;AACjB,UAAM,YAAuD;AAAA,MAC3D,MAAM;AAAA,MACN,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAEA,WAAO;AAAA,MACL,IAAI,QAAQ;AAAA,MACZ,cAAc,QAAQ,iBAAiB;AAAA,MACvC,MAAM,QAAQ;AAAA,MACd,QAAQ,UAAU,QAAQ,UAAU,MAAM,KAAK;AAAA,MAC/C,QAAQ,QAAQ,gBAAgB;AAAA,MAChC,UAAU,QAAQ,YAAY;AAAA,MAC9B,UAAW,QAAQ,YAAuC,CAAC;AAAA,IAC7D;AAAA,EACF;AAAA,EAEA,MAAc,gBAAgB,IAAsC;AAClE,UAAM,UAAU,MAAM,KAAK,OAAO,SAAS,SAAS,SAAS,EAAE;AAC/D,WAAO,KAAK,uBAAuB,OAAO;AAAA,EAC5C;AAAA,EAEA,MAAc,cAAc,IAAsC;AAChE,UAAM,UAAU,MAAM,KAAK,OAAO,SAAS,SAAS,OAAO,EAAE;AAC7D,WAAO,KAAK,uBAAuB,OAAO;AAAA,EAC5C;AAAA,EAEA,MAAc,cAAc,IAAiC;AAC3D,UAAM,QAAQ,MAAM,KAAK,OAAO,SAAS,SAAS,cAAc,EAAE;AAClE,WAAO,MAAM,KAAK,IAAI,CAAC,UAAU;AAAA,MAC/B,OAAO,KAAK,OAAO;AAAA,MACnB,UAAU,KAAK,YAAY;AAAA,IAC7B,EAAE;AAAA,EACJ;AAAA,EAEQ,sBACN,SACA,WACA,QACc;AACd,UAAM,QAAQ,KAAK,OAAO,SAAS,eAAe,SAAS,WAAW,MAAM;AAE5E,WAAO;AAAA,MACL,IAAI,MAAM;AAAA,MACV,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,MACZ,SAAS,MAAM;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,MAAc,eAAe,QAAiD;AAC5E,UAAM,WAAW,MAAM,KAAK,OAAO,UAAU,OAAO;AAAA,MAClD,OAAO,OAAO;AAAA,MACd,MAAM,OAAO;AAAA,MACb,UAAU,OAAO;AAAA,IACnB,CAAC;AAED,WAAO;AAAA,MACL,IAAI,SAAS;AAAA,MACb,OAAO,SAAS,SAAS,OAAO;AAAA,MAChC,WAAW,OAAO,MAAM,MAAM,GAAG,EAAE,CAAC;AAAA,MACpC,UAAU,OAAO,MAAM,MAAM,GAAG,EAAE,MAAM,CAAC,EAAE,KAAK,GAAG;AAAA,IACrD;AAAA,EACF;AAAA,EAEA,MAAc,iBAAiB,IAA+B;AAC5D,UAAM,WAAW,MAAM,KAAK,OAAO,UAAU,SAAS,EAAE;AAExD,QAAI,SAAS,SAAS;AACpB,YAAM,IAAI;AAAA,QACR,YAAY,EAAE;AAAA,QACd;AAAA,QACA,EAAE,MAAM,mBAAmB;AAAA,MAC7B;AAAA,IACF;AAEA,WAAO;AAAA,MACL,IAAI,SAAS;AAAA,MACb,OAAO,SAAS,SAAS;AAAA,MACzB,WAAW,SAAS,MAAM,MAAM,GAAG,EAAE,CAAC;AAAA,MACtC,UAAU,SAAS,MAAM,MAAM,GAAG,EAAE,MAAM,CAAC,EAAE,KAAK,GAAG;AAAA,IACvD;AAAA,EACF;AAAA,EAEA,MAAc,eACZ,IACA,QACmB;AACnB,UAAM,WAAW,MAAM,KAAK,OAAO,UAAU,OAAO,IAAI;AAAA,MACtD,OAAO,OAAO;AAAA,MACd,MAAM,OAAO;AAAA,MACb,UAAU,OAAO;AAAA,IACnB,CAAC;AAED,WAAO;AAAA,MACL,IAAI,SAAS;AAAA,MACb,OAAO,SAAS,SAAS;AAAA,MACzB,WAAW,SAAS,MAAM,MAAM,GAAG,EAAE,CAAC;AAAA,MACtC,UAAU,SAAS,MAAM,MAAM,GAAG,EAAE,MAAM,CAAC,EAAE,KAAK,GAAG;AAAA,IACvD;AAAA,EACF;AACF;","names":["Stripe"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { CreateSessionParams, CheckoutSessionResult, CheckoutSession, LineItem, WebhookEvent, CreateCustomerParams, Customer, UpdateCustomerParams } from '@flopay/shared';
|
|
2
|
+
|
|
3
|
+
/** Options for the FloPay Node SDK constructor. */
|
|
4
|
+
interface FloPayNodeOptions {
|
|
5
|
+
apiVersion?: string;
|
|
6
|
+
/** Stripe secret key, required when using Stripe-specific operations. */
|
|
7
|
+
stripeSecretKey?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Server-side FloPay SDK.
|
|
11
|
+
*
|
|
12
|
+
* Supports two modes of operation:
|
|
13
|
+
*
|
|
14
|
+
* 1. **Billing API mode** — Creates checkout sessions via the FloPay/ClickTech
|
|
15
|
+
* billing API (`POST /v1/checkouts/sessions`). This mirrors the
|
|
16
|
+
* `createCheckoutSession` function from `clicktech-core-ui`.
|
|
17
|
+
*
|
|
18
|
+
* 2. **Stripe direct mode** — When a `stripeSecretKey` is provided, also
|
|
19
|
+
* exposes direct Stripe operations (customers, webhooks).
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { FloPay } from '@flopay/node';
|
|
24
|
+
*
|
|
25
|
+
* const flopay = new FloPay('sk_test_...');
|
|
26
|
+
*
|
|
27
|
+
* // Create session via billing API
|
|
28
|
+
* const result = await flopay.checkout.sessions.create({
|
|
29
|
+
* billingApiUrl: 'https://billing.example.com',
|
|
30
|
+
* checkoutBaseUrl: 'https://checkout.example.com',
|
|
31
|
+
* clientId: 'client_123',
|
|
32
|
+
* items: [{ providerItemId: 'prod_abc', totalAmount: 49.99 }],
|
|
33
|
+
* account: { userId: 'user_1', email: 'user@example.com' },
|
|
34
|
+
* successUrl: '/success',
|
|
35
|
+
* cancelUrl: '/cancel',
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
declare class FloPay {
|
|
40
|
+
private readonly stripe;
|
|
41
|
+
readonly checkout: {
|
|
42
|
+
sessions: {
|
|
43
|
+
/** Create a checkout session via the billing API. */
|
|
44
|
+
create: (params: CreateSessionParams) => Promise<CheckoutSessionResult>;
|
|
45
|
+
/** Retrieve a checkout session from Stripe. */
|
|
46
|
+
retrieve: (id: string) => Promise<CheckoutSession>;
|
|
47
|
+
/** Expire a checkout session on Stripe. */
|
|
48
|
+
expire: (id: string) => Promise<CheckoutSession>;
|
|
49
|
+
/** List line items for a Stripe checkout session. */
|
|
50
|
+
listLineItems: (id: string) => Promise<LineItem[]>;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
readonly webhooks: {
|
|
54
|
+
constructEvent: (payload: string | Buffer, signature: string, secret: string) => WebhookEvent;
|
|
55
|
+
};
|
|
56
|
+
readonly customers: {
|
|
57
|
+
create: (params: CreateCustomerParams) => Promise<Customer>;
|
|
58
|
+
retrieve: (id: string) => Promise<Customer>;
|
|
59
|
+
update: (id: string, params: UpdateCustomerParams) => Promise<Customer>;
|
|
60
|
+
};
|
|
61
|
+
constructor(secretKey: string, options?: FloPayNodeOptions);
|
|
62
|
+
/**
|
|
63
|
+
* Create a checkout session via the billing API.
|
|
64
|
+
*
|
|
65
|
+
* Mirrors `createCheckoutSession` from `clicktech-core-ui/modules`.
|
|
66
|
+
* Posts to `{billingApiUrl}/v1/checkouts/sessions` and returns
|
|
67
|
+
* the session UUID for redirect.
|
|
68
|
+
*/
|
|
69
|
+
private createSession;
|
|
70
|
+
private normalizeStripeSession;
|
|
71
|
+
private retrieveSession;
|
|
72
|
+
private expireSession;
|
|
73
|
+
private listLineItems;
|
|
74
|
+
private constructWebhookEvent;
|
|
75
|
+
private createCustomer;
|
|
76
|
+
private retrieveCustomer;
|
|
77
|
+
private updateCustomer;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { FloPay, type FloPayNodeOptions };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { CreateSessionParams, CheckoutSessionResult, CheckoutSession, LineItem, WebhookEvent, CreateCustomerParams, Customer, UpdateCustomerParams } from '@flopay/shared';
|
|
2
|
+
|
|
3
|
+
/** Options for the FloPay Node SDK constructor. */
|
|
4
|
+
interface FloPayNodeOptions {
|
|
5
|
+
apiVersion?: string;
|
|
6
|
+
/** Stripe secret key, required when using Stripe-specific operations. */
|
|
7
|
+
stripeSecretKey?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Server-side FloPay SDK.
|
|
11
|
+
*
|
|
12
|
+
* Supports two modes of operation:
|
|
13
|
+
*
|
|
14
|
+
* 1. **Billing API mode** — Creates checkout sessions via the FloPay/ClickTech
|
|
15
|
+
* billing API (`POST /v1/checkouts/sessions`). This mirrors the
|
|
16
|
+
* `createCheckoutSession` function from `clicktech-core-ui`.
|
|
17
|
+
*
|
|
18
|
+
* 2. **Stripe direct mode** — When a `stripeSecretKey` is provided, also
|
|
19
|
+
* exposes direct Stripe operations (customers, webhooks).
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { FloPay } from '@flopay/node';
|
|
24
|
+
*
|
|
25
|
+
* const flopay = new FloPay('sk_test_...');
|
|
26
|
+
*
|
|
27
|
+
* // Create session via billing API
|
|
28
|
+
* const result = await flopay.checkout.sessions.create({
|
|
29
|
+
* billingApiUrl: 'https://billing.example.com',
|
|
30
|
+
* checkoutBaseUrl: 'https://checkout.example.com',
|
|
31
|
+
* clientId: 'client_123',
|
|
32
|
+
* items: [{ providerItemId: 'prod_abc', totalAmount: 49.99 }],
|
|
33
|
+
* account: { userId: 'user_1', email: 'user@example.com' },
|
|
34
|
+
* successUrl: '/success',
|
|
35
|
+
* cancelUrl: '/cancel',
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
declare class FloPay {
|
|
40
|
+
private readonly stripe;
|
|
41
|
+
readonly checkout: {
|
|
42
|
+
sessions: {
|
|
43
|
+
/** Create a checkout session via the billing API. */
|
|
44
|
+
create: (params: CreateSessionParams) => Promise<CheckoutSessionResult>;
|
|
45
|
+
/** Retrieve a checkout session from Stripe. */
|
|
46
|
+
retrieve: (id: string) => Promise<CheckoutSession>;
|
|
47
|
+
/** Expire a checkout session on Stripe. */
|
|
48
|
+
expire: (id: string) => Promise<CheckoutSession>;
|
|
49
|
+
/** List line items for a Stripe checkout session. */
|
|
50
|
+
listLineItems: (id: string) => Promise<LineItem[]>;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
readonly webhooks: {
|
|
54
|
+
constructEvent: (payload: string | Buffer, signature: string, secret: string) => WebhookEvent;
|
|
55
|
+
};
|
|
56
|
+
readonly customers: {
|
|
57
|
+
create: (params: CreateCustomerParams) => Promise<Customer>;
|
|
58
|
+
retrieve: (id: string) => Promise<Customer>;
|
|
59
|
+
update: (id: string, params: UpdateCustomerParams) => Promise<Customer>;
|
|
60
|
+
};
|
|
61
|
+
constructor(secretKey: string, options?: FloPayNodeOptions);
|
|
62
|
+
/**
|
|
63
|
+
* Create a checkout session via the billing API.
|
|
64
|
+
*
|
|
65
|
+
* Mirrors `createCheckoutSession` from `clicktech-core-ui/modules`.
|
|
66
|
+
* Posts to `{billingApiUrl}/v1/checkouts/sessions` and returns
|
|
67
|
+
* the session UUID for redirect.
|
|
68
|
+
*/
|
|
69
|
+
private createSession;
|
|
70
|
+
private normalizeStripeSession;
|
|
71
|
+
private retrieveSession;
|
|
72
|
+
private expireSession;
|
|
73
|
+
private listLineItems;
|
|
74
|
+
private constructWebhookEvent;
|
|
75
|
+
private createCustomer;
|
|
76
|
+
private retrieveCustomer;
|
|
77
|
+
private updateCustomer;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { FloPay, type FloPayNodeOptions };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
// src/flopay-node.ts
|
|
2
|
+
import Stripe from "stripe";
|
|
3
|
+
import { FloPayError, SDK_VERSION } from "@flopay/shared";
|
|
4
|
+
var FloPay = class {
|
|
5
|
+
constructor(secretKey, options) {
|
|
6
|
+
if (!secretKey) {
|
|
7
|
+
throw new FloPayError(
|
|
8
|
+
"A secret key is required to initialize the FloPay server SDK.",
|
|
9
|
+
"authentication_error"
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
this.stripe = new Stripe(secretKey, {
|
|
13
|
+
apiVersion: "2025-04-30.basil",
|
|
14
|
+
appInfo: {
|
|
15
|
+
name: "flopay-node",
|
|
16
|
+
version: SDK_VERSION
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
this.checkout = {
|
|
20
|
+
sessions: {
|
|
21
|
+
create: this.createSession.bind(this),
|
|
22
|
+
retrieve: this.retrieveSession.bind(this),
|
|
23
|
+
expire: this.expireSession.bind(this),
|
|
24
|
+
listLineItems: this.listLineItems.bind(this)
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
this.webhooks = {
|
|
28
|
+
constructEvent: this.constructWebhookEvent.bind(this)
|
|
29
|
+
};
|
|
30
|
+
this.customers = {
|
|
31
|
+
create: this.createCustomer.bind(this),
|
|
32
|
+
retrieve: this.retrieveCustomer.bind(this),
|
|
33
|
+
update: this.updateCustomer.bind(this)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Create a checkout session via the billing API.
|
|
38
|
+
*
|
|
39
|
+
* Mirrors `createCheckoutSession` from `clicktech-core-ui/modules`.
|
|
40
|
+
* Posts to `{billingApiUrl}/v1/checkouts/sessions` and returns
|
|
41
|
+
* the session UUID for redirect.
|
|
42
|
+
*/
|
|
43
|
+
async createSession(params) {
|
|
44
|
+
const {
|
|
45
|
+
billingApiUrl,
|
|
46
|
+
checkoutBaseUrl,
|
|
47
|
+
items = [],
|
|
48
|
+
subscriptions = [],
|
|
49
|
+
account,
|
|
50
|
+
successUrl,
|
|
51
|
+
cancelUrl,
|
|
52
|
+
checkoutMode = "confirm",
|
|
53
|
+
couponCodes = [],
|
|
54
|
+
tagsData,
|
|
55
|
+
redirectParams = {},
|
|
56
|
+
timeoutMs = 12e3,
|
|
57
|
+
clientId,
|
|
58
|
+
utmMetadata
|
|
59
|
+
} = params;
|
|
60
|
+
const payload = {
|
|
61
|
+
clientId,
|
|
62
|
+
successUrl,
|
|
63
|
+
cancelUrl,
|
|
64
|
+
checkoutMode,
|
|
65
|
+
items: items.map((item) => ({
|
|
66
|
+
providerItemId: item.providerItemId,
|
|
67
|
+
providerItemName: item.providerItemName ?? null,
|
|
68
|
+
quantity: item.quantity ?? 1,
|
|
69
|
+
totalAmount: item.totalAmount,
|
|
70
|
+
overrideAmount: item.overrideAmount ?? null,
|
|
71
|
+
currency: item.currency ?? "USD"
|
|
72
|
+
})),
|
|
73
|
+
subscriptions: subscriptions.map((sub) => ({
|
|
74
|
+
providerPlanId: sub.providerPlanId,
|
|
75
|
+
providerPlanName: sub.providerPlanName ?? null,
|
|
76
|
+
quantity: sub.quantity ?? 1,
|
|
77
|
+
totalAmount: sub.totalAmount,
|
|
78
|
+
overrideAmount: sub.overrideAmount ?? null,
|
|
79
|
+
currency: sub.currency ?? "USD"
|
|
80
|
+
})),
|
|
81
|
+
accountData: {
|
|
82
|
+
userId: account.userId,
|
|
83
|
+
firstName: account.firstName ?? null,
|
|
84
|
+
lastName: account.lastName ?? null,
|
|
85
|
+
email: account.email,
|
|
86
|
+
country: account.country ?? null,
|
|
87
|
+
gender: account.gender ?? null,
|
|
88
|
+
city: account.city ?? null,
|
|
89
|
+
state: account.state ?? null,
|
|
90
|
+
zip: account.zip ?? null
|
|
91
|
+
},
|
|
92
|
+
couponCodes
|
|
93
|
+
};
|
|
94
|
+
if (tagsData) {
|
|
95
|
+
payload["tagsData"] = tagsData;
|
|
96
|
+
}
|
|
97
|
+
if (utmMetadata?.length) {
|
|
98
|
+
payload["utmMetadata"] = utmMetadata;
|
|
99
|
+
}
|
|
100
|
+
const url = `${billingApiUrl.replace(/\/+$/, "")}/v1/checkouts/sessions`;
|
|
101
|
+
const controller = new AbortController();
|
|
102
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
103
|
+
let status;
|
|
104
|
+
let body;
|
|
105
|
+
try {
|
|
106
|
+
const response = await fetch(url, {
|
|
107
|
+
method: "POST",
|
|
108
|
+
headers: { "Content-Type": "application/json" },
|
|
109
|
+
body: JSON.stringify(payload),
|
|
110
|
+
signal: controller.signal
|
|
111
|
+
});
|
|
112
|
+
status = response.status;
|
|
113
|
+
try {
|
|
114
|
+
body = await response.json();
|
|
115
|
+
} catch {
|
|
116
|
+
}
|
|
117
|
+
} finally {
|
|
118
|
+
clearTimeout(timer);
|
|
119
|
+
}
|
|
120
|
+
if (status === 201) {
|
|
121
|
+
const uuid = body?.data?.uuid;
|
|
122
|
+
if (!uuid) {
|
|
123
|
+
throw new Error("Checkout session created but no UUID was returned by the billing API");
|
|
124
|
+
}
|
|
125
|
+
const redirectUrl = new URL(`${checkoutBaseUrl.replace(/\/+$/, "")}/secure`);
|
|
126
|
+
redirectUrl.searchParams.set("id", uuid);
|
|
127
|
+
for (const [key, value] of Object.entries(redirectParams)) {
|
|
128
|
+
redirectUrl.searchParams.set(key, value);
|
|
129
|
+
}
|
|
130
|
+
return { status: 201, redirectUrl: redirectUrl.toString() };
|
|
131
|
+
}
|
|
132
|
+
if (status === 204) {
|
|
133
|
+
return { status: 204 };
|
|
134
|
+
}
|
|
135
|
+
return { status };
|
|
136
|
+
}
|
|
137
|
+
normalizeStripeSession(session) {
|
|
138
|
+
const statusMap = {
|
|
139
|
+
open: "open",
|
|
140
|
+
complete: "complete",
|
|
141
|
+
expired: "expired"
|
|
142
|
+
};
|
|
143
|
+
return {
|
|
144
|
+
id: session.id,
|
|
145
|
+
clientSecret: session.client_secret ?? "",
|
|
146
|
+
mode: session.mode,
|
|
147
|
+
status: statusMap[session.status ?? "open"] ?? "open",
|
|
148
|
+
amount: session.amount_total ?? 0,
|
|
149
|
+
currency: session.currency ?? "usd",
|
|
150
|
+
metadata: session.metadata ?? {}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
async retrieveSession(id) {
|
|
154
|
+
const session = await this.stripe.checkout.sessions.retrieve(id);
|
|
155
|
+
return this.normalizeStripeSession(session);
|
|
156
|
+
}
|
|
157
|
+
async expireSession(id) {
|
|
158
|
+
const session = await this.stripe.checkout.sessions.expire(id);
|
|
159
|
+
return this.normalizeStripeSession(session);
|
|
160
|
+
}
|
|
161
|
+
async listLineItems(id) {
|
|
162
|
+
const items = await this.stripe.checkout.sessions.listLineItems(id);
|
|
163
|
+
return items.data.map((item) => ({
|
|
164
|
+
price: item.price?.id,
|
|
165
|
+
quantity: item.quantity ?? 1
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
constructWebhookEvent(payload, signature, secret) {
|
|
169
|
+
const event = this.stripe.webhooks.constructEvent(payload, signature, secret);
|
|
170
|
+
return {
|
|
171
|
+
id: event.id,
|
|
172
|
+
type: event.type,
|
|
173
|
+
data: event.data,
|
|
174
|
+
created: event.created
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
async createCustomer(params) {
|
|
178
|
+
const customer = await this.stripe.customers.create({
|
|
179
|
+
email: params.email,
|
|
180
|
+
name: params.name,
|
|
181
|
+
metadata: params.metadata
|
|
182
|
+
});
|
|
183
|
+
return {
|
|
184
|
+
id: customer.id,
|
|
185
|
+
email: customer.email ?? params.email,
|
|
186
|
+
firstName: params.name?.split(" ")[0],
|
|
187
|
+
lastName: params.name?.split(" ").slice(1).join(" ")
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
async retrieveCustomer(id) {
|
|
191
|
+
const customer = await this.stripe.customers.retrieve(id);
|
|
192
|
+
if (customer.deleted) {
|
|
193
|
+
throw new FloPayError(
|
|
194
|
+
`Customer ${id} has been deleted`,
|
|
195
|
+
"api_error",
|
|
196
|
+
{ code: "resource_missing" }
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
id: customer.id,
|
|
201
|
+
email: customer.email ?? "",
|
|
202
|
+
firstName: customer.name?.split(" ")[0],
|
|
203
|
+
lastName: customer.name?.split(" ").slice(1).join(" ")
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
async updateCustomer(id, params) {
|
|
207
|
+
const customer = await this.stripe.customers.update(id, {
|
|
208
|
+
email: params.email,
|
|
209
|
+
name: params.name,
|
|
210
|
+
metadata: params.metadata
|
|
211
|
+
});
|
|
212
|
+
return {
|
|
213
|
+
id: customer.id,
|
|
214
|
+
email: customer.email ?? "",
|
|
215
|
+
firstName: customer.name?.split(" ")[0],
|
|
216
|
+
lastName: customer.name?.split(" ").slice(1).join(" ")
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
export {
|
|
221
|
+
FloPay
|
|
222
|
+
};
|
|
223
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/flopay-node.ts"],"sourcesContent":["import Stripe from 'stripe';\nimport type {\n CheckoutSession,\n LineItem,\n CreateSessionParams,\n CheckoutSessionResult,\n CreateCustomerParams,\n UpdateCustomerParams,\n WebhookEvent,\n Customer,\n} from '@flopay/shared';\nimport { FloPayError, SDK_VERSION } from '@flopay/shared';\n\n/** Options for the FloPay Node SDK constructor. */\nexport interface FloPayNodeOptions {\n apiVersion?: string;\n /** Stripe secret key, required when using Stripe-specific operations. */\n stripeSecretKey?: string;\n}\n\n/**\n * Server-side FloPay SDK.\n *\n * Supports two modes of operation:\n *\n * 1. **Billing API mode** — Creates checkout sessions via the FloPay/ClickTech\n * billing API (`POST /v1/checkouts/sessions`). This mirrors the\n * `createCheckoutSession` function from `clicktech-core-ui`.\n *\n * 2. **Stripe direct mode** — When a `stripeSecretKey` is provided, also\n * exposes direct Stripe operations (customers, webhooks).\n *\n * @example\n * ```ts\n * import { FloPay } from '@flopay/node';\n *\n * const flopay = new FloPay('sk_test_...');\n *\n * // Create session via billing API\n * const result = await flopay.checkout.sessions.create({\n * billingApiUrl: 'https://billing.example.com',\n * checkoutBaseUrl: 'https://checkout.example.com',\n * clientId: 'client_123',\n * items: [{ providerItemId: 'prod_abc', totalAmount: 49.99 }],\n * account: { userId: 'user_1', email: 'user@example.com' },\n * successUrl: '/success',\n * cancelUrl: '/cancel',\n * });\n * ```\n */\nexport class FloPay {\n private readonly stripe: Stripe;\n\n readonly checkout: {\n sessions: {\n /** Create a checkout session via the billing API. */\n create: (params: CreateSessionParams) => Promise<CheckoutSessionResult>;\n /** Retrieve a checkout session from Stripe. */\n retrieve: (id: string) => Promise<CheckoutSession>;\n /** Expire a checkout session on Stripe. */\n expire: (id: string) => Promise<CheckoutSession>;\n /** List line items for a Stripe checkout session. */\n listLineItems: (id: string) => Promise<LineItem[]>;\n };\n };\n\n readonly webhooks: {\n constructEvent: (\n payload: string | Buffer,\n signature: string,\n secret: string,\n ) => WebhookEvent;\n };\n\n readonly customers: {\n create: (params: CreateCustomerParams) => Promise<Customer>;\n retrieve: (id: string) => Promise<Customer>;\n update: (id: string, params: UpdateCustomerParams) => Promise<Customer>;\n };\n\n constructor(secretKey: string, options?: FloPayNodeOptions) {\n if (!secretKey) {\n throw new FloPayError(\n 'A secret key is required to initialize the FloPay server SDK.',\n 'authentication_error',\n );\n }\n\n this.stripe = new Stripe(secretKey, {\n apiVersion: '2025-04-30.basil' as Stripe.LatestApiVersion,\n appInfo: {\n name: 'flopay-node',\n version: SDK_VERSION,\n },\n });\n\n this.checkout = {\n sessions: {\n create: this.createSession.bind(this),\n retrieve: this.retrieveSession.bind(this),\n expire: this.expireSession.bind(this),\n listLineItems: this.listLineItems.bind(this),\n },\n };\n\n this.webhooks = {\n constructEvent: this.constructWebhookEvent.bind(this),\n };\n\n this.customers = {\n create: this.createCustomer.bind(this),\n retrieve: this.retrieveCustomer.bind(this),\n update: this.updateCustomer.bind(this),\n };\n }\n\n /**\n * Create a checkout session via the billing API.\n *\n * Mirrors `createCheckoutSession` from `clicktech-core-ui/modules`.\n * Posts to `{billingApiUrl}/v1/checkouts/sessions` and returns\n * the session UUID for redirect.\n */\n private async createSession(\n params: CreateSessionParams,\n ): Promise<CheckoutSessionResult> {\n const {\n billingApiUrl,\n checkoutBaseUrl,\n items = [],\n subscriptions = [],\n account,\n successUrl,\n cancelUrl,\n checkoutMode = 'confirm',\n couponCodes = [],\n tagsData,\n redirectParams = {},\n timeoutMs = 12000,\n clientId,\n utmMetadata,\n } = params;\n\n const payload: Record<string, unknown> = {\n clientId,\n successUrl,\n cancelUrl,\n checkoutMode,\n items: items.map((item) => ({\n providerItemId: item.providerItemId,\n providerItemName: item.providerItemName ?? null,\n quantity: item.quantity ?? 1,\n totalAmount: item.totalAmount,\n overrideAmount: item.overrideAmount ?? null,\n currency: item.currency ?? 'USD',\n })),\n subscriptions: subscriptions.map((sub) => ({\n providerPlanId: sub.providerPlanId,\n providerPlanName: sub.providerPlanName ?? null,\n quantity: sub.quantity ?? 1,\n totalAmount: sub.totalAmount,\n overrideAmount: sub.overrideAmount ?? null,\n currency: sub.currency ?? 'USD',\n })),\n accountData: {\n userId: account.userId,\n firstName: account.firstName ?? null,\n lastName: account.lastName ?? null,\n email: account.email,\n country: account.country ?? null,\n gender: account.gender ?? null,\n city: account.city ?? null,\n state: account.state ?? null,\n zip: account.zip ?? null,\n },\n couponCodes,\n };\n\n if (tagsData) {\n payload['tagsData'] = tagsData;\n }\n\n if (utmMetadata?.length) {\n payload['utmMetadata'] = utmMetadata;\n }\n\n const url = `${billingApiUrl.replace(/\\/+$/, '')}/v1/checkouts/sessions`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let status: number;\n let body: { data?: { uuid?: string } } | undefined;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n\n status = response.status;\n\n try {\n body = await response.json() as { data?: { uuid?: string } };\n } catch {\n // 204 or empty body\n }\n } finally {\n clearTimeout(timer);\n }\n\n if (status === 201) {\n const uuid = body?.data?.uuid;\n\n if (!uuid) {\n throw new Error('Checkout session created but no UUID was returned by the billing API');\n }\n\n const redirectUrl = new URL(`${checkoutBaseUrl.replace(/\\/+$/, '')}/secure`);\n redirectUrl.searchParams.set('id', uuid);\n\n for (const [key, value] of Object.entries(redirectParams)) {\n redirectUrl.searchParams.set(key, value);\n }\n\n return { status: 201, redirectUrl: redirectUrl.toString() };\n }\n\n if (status === 204) {\n return { status: 204 };\n }\n\n return { status };\n }\n\n private normalizeStripeSession(\n session: Stripe.Checkout.Session,\n ): CheckoutSession {\n const statusMap: Record<string, CheckoutSession['status']> = {\n open: 'open',\n complete: 'complete',\n expired: 'expired',\n };\n\n return {\n id: session.id,\n clientSecret: session.client_secret ?? '',\n mode: session.mode as CheckoutSession['mode'],\n status: statusMap[session.status ?? 'open'] ?? 'open',\n amount: session.amount_total ?? 0,\n currency: session.currency ?? 'usd',\n metadata: (session.metadata as Record<string, string>) ?? {},\n };\n }\n\n private async retrieveSession(id: string): Promise<CheckoutSession> {\n const session = await this.stripe.checkout.sessions.retrieve(id);\n return this.normalizeStripeSession(session);\n }\n\n private async expireSession(id: string): Promise<CheckoutSession> {\n const session = await this.stripe.checkout.sessions.expire(id);\n return this.normalizeStripeSession(session);\n }\n\n private async listLineItems(id: string): Promise<LineItem[]> {\n const items = await this.stripe.checkout.sessions.listLineItems(id);\n return items.data.map((item) => ({\n price: item.price?.id,\n quantity: item.quantity ?? 1,\n }));\n }\n\n private constructWebhookEvent(\n payload: string | Buffer,\n signature: string,\n secret: string,\n ): WebhookEvent {\n const event = this.stripe.webhooks.constructEvent(payload, signature, secret);\n\n return {\n id: event.id,\n type: event.type,\n data: event.data as unknown as Record<string, unknown>,\n created: event.created,\n };\n }\n\n private async createCustomer(params: CreateCustomerParams): Promise<Customer> {\n const customer = await this.stripe.customers.create({\n email: params.email,\n name: params.name,\n metadata: params.metadata,\n });\n\n return {\n id: customer.id,\n email: customer.email ?? params.email,\n firstName: params.name?.split(' ')[0],\n lastName: params.name?.split(' ').slice(1).join(' '),\n };\n }\n\n private async retrieveCustomer(id: string): Promise<Customer> {\n const customer = await this.stripe.customers.retrieve(id);\n\n if (customer.deleted) {\n throw new FloPayError(\n `Customer ${id} has been deleted`,\n 'api_error',\n { code: 'resource_missing' },\n );\n }\n\n return {\n id: customer.id,\n email: customer.email ?? '',\n firstName: customer.name?.split(' ')[0],\n lastName: customer.name?.split(' ').slice(1).join(' '),\n };\n }\n\n private async updateCustomer(\n id: string,\n params: UpdateCustomerParams,\n ): Promise<Customer> {\n const customer = await this.stripe.customers.update(id, {\n email: params.email,\n name: params.name,\n metadata: params.metadata,\n });\n\n return {\n id: customer.id,\n email: customer.email ?? '',\n firstName: customer.name?.split(' ')[0],\n lastName: customer.name?.split(' ').slice(1).join(' '),\n };\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flopay/node",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"registry": "https://registry.npmjs.org/",
|
|
7
|
+
"access": "restricted"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/flopay/flopay.git",
|
|
12
|
+
"directory": "packages/node"
|
|
13
|
+
},
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.mjs",
|
|
18
|
+
"require": "./dist/index.cjs"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"main": "./dist/index.cjs",
|
|
22
|
+
"module": "./dist/index.mjs",
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"stripe": "^17.0.0",
|
|
29
|
+
"@flopay/shared": "0.1.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"typescript": "^5.7.0",
|
|
33
|
+
"tsup": "^8.3.0",
|
|
34
|
+
"vitest": "^2.1.0"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsup",
|
|
38
|
+
"dev": "tsup --watch",
|
|
39
|
+
"typecheck": "tsc --noEmit",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"clean": "rm -rf dist"
|
|
42
|
+
}
|
|
43
|
+
}
|