@paygentic/wrap 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Paygentic
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,266 @@
1
+ # Wrap SDK
2
+
3
+ Wrap Claude agent queries with automatic payment tracking and usage metering.
4
+
5
+ **Paygentic Platform:** [Production](https://platform.paygentic.io) | [Sandbox](https://platform.sandbox.paygentic.io)
6
+
7
+ ## Quick Start
8
+
9
+ ```typescript
10
+ import { createWrapClient } from "@paygentic/wrap";
11
+
12
+ const client = createWrapClient();
13
+ const plan = await client.plans.init();
14
+ const query = plan.wrap("customer_123");
15
+
16
+ // wrapped query supports all claude agent sdk options
17
+ const response = await query({
18
+ model: "claude-sonnet-4-20250514",
19
+ prompt: "Hello, world!",
20
+ });
21
+
22
+ for await (const message of response) {
23
+ // Process messages as usual
24
+ // Usage is automatically tracked on completion
25
+ }
26
+ ```
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ npm install @paygentic/wrap
32
+ ```
33
+
34
+ ## CLI Setup
35
+
36
+ The package includes an interactive setup command that creates the necessary Paygentic resources (product, billable metric, price, and plan) for your agent:
37
+
38
+ ```bash
39
+ npx @paygentic/wrap setup
40
+ ```
41
+
42
+ ### What It Does
43
+
44
+ The setup wizard will:
45
+
46
+ 1. **Prompt for configuration:**
47
+ - Environment (`sandbox` or `prod`)
48
+ - API Key (from [Paygentic Platform](https://platform.paygentic.io/merchant/developer/apikeys))
49
+ - Merchant ID (from [Organization Settings](https://platform.paygentic.io/merchant/settings/organization))
50
+ - Agent name (used for product naming)
51
+ - Payment type (`instant` or `post-paid`)
52
+ - Max expected usage per query (USD)
53
+ - Margin percentage (markup on usage costs)
54
+
55
+ 2. **Create Paygentic resources:**
56
+ - Product for your agent
57
+ - Billable metric for tracking usage
58
+ - Price with your configured margin
59
+ - Plan linking everything together
60
+
61
+ 3. **Output environment variables** to add to your `.env` file
62
+
63
+ ### Example Session
64
+
65
+ ```
66
+ === Paygentic Agent Billing Setup ===
67
+
68
+ Environment (sandbox/prod) [sandbox]: sandbox
69
+ Get your API key from: https://platform.sandbox.paygentic.io/merchant/developer/apikeys
70
+ API Key: pk_sandbox_xxxxx
71
+ Merchant ID: mer_xxxxx
72
+ Agent Name: My AI Assistant
73
+ Payment Type (instant/post-paid): instant
74
+ Max expected usage per query (USD) [2.00]: 2.00
75
+ Margin percentage [10]: 15
76
+
77
+ --- Creating Paygentic resources ---
78
+
79
+ Creating product...
80
+ Creating billable metric...
81
+ Creating price...
82
+ Creating plan...
83
+
84
+ === Setup complete! ===
85
+
86
+ Add these to your .env file:
87
+
88
+ PAYGENTIC_WRAP_ENV=sandbox
89
+ PAYGENTIC_WRAP_API_KEY=pk_sandbox_xxxxx
90
+ PAYGENTIC_WRAP_MERCHANT_ID=mer_xxxxx
91
+ PAYGENTIC_WRAP_PRODUCT_ID=prod_xxxxx
92
+ PAYGENTIC_WRAP_PLAN_ID=plan_xxxxx
93
+ PAYGENTIC_WRAP_PRICE_ID=price_xxxxx
94
+ PAYGENTIC_WRAP_BILLABLE_METRIC_ID=bm_xxxxx
95
+ ```
96
+
97
+ ## Environment Variables
98
+
99
+ The SDK reads these environment variables as defaults:
100
+
101
+ | Variable | Description | Required |
102
+ |----------|-------------|----------|
103
+ | `PAYGENTIC_WRAP_API_KEY` | Your Paygentic API key | Yes (or pass `apiKey` option) |
104
+ | `PAYGENTIC_WRAP_ENV` | Environment: `prod` or `sandbox` | No (defaults to `prod`) |
105
+ | `PAYGENTIC_WRAP_MERCHANT_ID` | Your merchant ID | For setup script |
106
+ | `PAYGENTIC_WRAP_PLAN_ID` | Your plan ID | For your app |
107
+ | `PAYGENTIC_WRAP_PRICE_ID` | Your price ID | For your app |
108
+ | `PAYGENTIC_WRAP_PRODUCT_ID` | Your product ID | For reference |
109
+ | `PAYGENTIC_WRAP_BILLABLE_METRIC_ID` | Your billable metric ID | For reference |
110
+
111
+ Run `npx @paygentic/wrap setup` to create these resources and get the environment variables.
112
+
113
+ ## How It Works
114
+
115
+ When you call `wrap`, the SDK:
116
+
117
+ 1. Verifies the customer has an active subscription
118
+ 2. Sets a safe budget limit (90% of maxPrice or maxPrice - $0.10)
119
+ 3. Reserves funds for instant payments
120
+ 4. Tracks usage and creates billing/payment events on completion
121
+ 5. Handles overages by splitting into multiple events if needed
122
+
123
+ ## Pricing Models
124
+
125
+ ### Dynamic Pricing
126
+
127
+ Only pass through usage cost to end user:
128
+
129
+ ```typescript
130
+ // Price config: maxPrice >= $0.20
131
+ const plan = await client.plans.init({
132
+ planId: "plan_abc123",
133
+ priceId: "price_dynamic",
134
+ });
135
+ ```
136
+
137
+ ### Percentage Pricing
138
+
139
+ Apply a markup to usage cost:
140
+
141
+ ```typescript
142
+ // Price config: percentage, minCharge, maxCharge (maxCharge >= $0.20)
143
+ // Plan and price id default to PAYGENTIC_WRAP_PLAN_ID resp PAYGENTIC_WRAP_PRICE_ID
144
+ const plan = await client.plans.init({
145
+ planId: "plan_abc123",
146
+ priceId: "price_percentage",
147
+ });
148
+ ```
149
+
150
+ ## Subscriptions
151
+
152
+ ### Create a Customer and Subscription
153
+
154
+ ```typescript
155
+ const result = await client.subscriptions.create({
156
+ customer: {
157
+ name: "Example Inc",
158
+ email: "billing@example.com",
159
+ address: {
160
+ line1: "123 Main St",
161
+ city: "San Francisco",
162
+ state: "CA",
163
+ postalCode: "94105",
164
+ country: "US",
165
+ },
166
+ },
167
+ minWalletAmount: "20.00",
168
+ redirectUrls: {
169
+ onSuccess: "https://example.com/success",
170
+ onFailure: "https://example.com/failure",
171
+ },
172
+ });
173
+
174
+ const { customerId, subscriptionDetail } = result;
175
+ ```
176
+
177
+ ### Get Customer Portal URL
178
+
179
+ ```typescript
180
+ const portal = await client.subscriptions.portal(subscriptionId);
181
+ // portal.url - redirect customers here to manage their subscription
182
+ // portal.expiresAt - URL expiration time
183
+ ```
184
+
185
+ ### Find Customer by Email
186
+
187
+ ```typescript
188
+ const customerId = await client.subscriptions.findCustomer("billing@example.com");
189
+ ```
190
+
191
+ ## Payment Terms
192
+
193
+ - **Pre-paid** (default): Creates an entitlement before the query runs
194
+ - **In-arrears**: Records usage after the query completes
195
+
196
+ ## Error Handling
197
+
198
+ ### Insufficient Funds
199
+
200
+ When a customer doesn't have enough balance to process a query, the SDK throws an `InsufficientFundsError` with a portal URL so they can top up:
201
+
202
+ ```typescript
203
+ import { createWrapClient, InsufficientFundsError } from "@paygentic/wrap";
204
+
205
+ const client = createWrapClient();
206
+ const plan = await client.plans.init();
207
+ const query = plan.wrap("customer_123");
208
+
209
+ try {
210
+ const response = await query({ model: "claude-sonnet-4-20250514", prompt: "Hello" });
211
+ for await (const message of response) {
212
+ // Process messages
213
+ }
214
+ } catch (error) {
215
+ if (error instanceof InsufficientFundsError) {
216
+ console.log("Please top up your balance:", error.portalUrl);
217
+ console.log("Link expires at:", error.portalExpiresAt);
218
+ console.log("Subscription ID:", error.subscriptionId);
219
+ // Redirect customer to error.portalUrl to add funds
220
+ } else {
221
+ throw error;
222
+ }
223
+ }
224
+ ```
225
+
226
+ The `InsufficientFundsError` includes:
227
+
228
+ - `portalUrl` - URL where the customer can add funds to their wallet
229
+ - `portalExpiresAt` - When the portal URL expires (ISO timestamp)
230
+ - `subscriptionId` - The subscription that needs funding
231
+
232
+ ### Query Stopped Due to Budget Limit
233
+
234
+ The SDK automatically sets a budget limit based on your pricing config to protect against runaway costs. When a query exceeds this limit, the agent stops and the result message has `subtype: "error_max_budget_usd"`.
235
+
236
+ You can resume the query by passing the `session_id` in the `options.resume` field:
237
+
238
+ ```typescript
239
+ let sessionId: string | undefined;
240
+
241
+ const response = await query({ model: "claude-sonnet-4-20250514", prompt: "Complex task" });
242
+
243
+ for await (const message of response) {
244
+ if (message.type === "result") {
245
+ sessionId = message.session_id;
246
+
247
+ if (message.subtype === "error_max_budget_usd") {
248
+ console.log("Query stopped due to budget limit");
249
+ console.log("Cost incurred:", message.total_cost_usd);
250
+ }
251
+ }
252
+ }
253
+
254
+ // Resume the query if it stopped due to budget limit
255
+ if (sessionId) {
256
+ const continuation = await query({
257
+ model: "claude-sonnet-4-20250514",
258
+ prompt: "Please continue",
259
+ options: { resume: sessionId },
260
+ });
261
+
262
+ for await (const message of continuation) {
263
+ // Billing is handled automatically for the continuation
264
+ }
265
+ }
266
+ ```
@@ -0,0 +1,5 @@
1
+ import { paths } from "./schema.js";
2
+ type ApiEnv = 'prod' | 'sandbox' | 'local';
3
+ export declare function createApiClient(apiKey: string, env?: ApiEnv): import("openapi-fetch").Client<paths, `${string}/${string}`>;
4
+ export {};
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,KAAK,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAO3C,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,GAAE,MAAe,gEAQnE"}
@@ -0,0 +1,16 @@
1
+ import createClient from "openapi-fetch";
2
+ const baseUrlMap = {
3
+ prod: 'https://api.paygentic.io',
4
+ sandbox: 'https://api.sandbox.paygentic.io',
5
+ local: 'http://localhost:8200',
6
+ };
7
+ export function createApiClient(apiKey, env = 'prod') {
8
+ const baseUrl = baseUrlMap[env] || baseUrlMap.prod;
9
+ return createClient({
10
+ baseUrl,
11
+ headers: {
12
+ Authorization: `Bearer ${apiKey}`,
13
+ },
14
+ });
15
+ }
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,eAAe,CAAC;AAIzC,MAAM,UAAU,GAA2B;IACzC,IAAI,EAAE,0BAA0B;IAChC,OAAO,EAAE,kCAAkC;IAC3C,KAAK,EAAE,uBAAuB;CAC/B,CAAA;AAED,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,MAAc,MAAM;IAClE,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC;IACnD,OAAO,YAAY,CAAQ;QACzB,OAAO;QACP,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,MAAM,EAAE;SAClC;KACF,CAAC,CAAC;AACL,CAAC"}