@ledewire/browser 0.6.1 → 0.8.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 CHANGED
@@ -16,7 +16,7 @@ Browser SDK for the [LedeWire](https://api.ledewire.com/api-docs/index.html) con
16
16
  // Determine what the visitor needs to do next
17
17
  const state = await lw.checkout.state('content-id')
18
18
  // state.checkout_state.next_required_action:
19
- // 'authenticate' | 'fund_wallet' | 'purchase' | 'view_content'
19
+ // 'authenticate' | 'fund_wallet' | 'purchase'
20
20
  </script>
21
21
  ```
22
22
 
@@ -48,6 +48,8 @@ const lw = init({
48
48
  | `lw.purchases` | List and create content purchases |
49
49
  | `lw.content` | Fetch content with buyer access info |
50
50
  | `lw.checkout` | Checkout state — what action is required next |
51
+ | `lw.user.spendCap` | Buyer's daily spend cap — read and update the ceiling |
52
+ | `lw.user.mcpKeys` | Manage buyer MCP API keys for the Ledewire MCP server |
51
53
  | `lw.seller.content` | List, search, and get store content (API key auth) |
52
54
 
53
55
  ## Example: Fetch Google OAuth Client ID Before Sign-In
@@ -63,6 +65,11 @@ google.accounts.id.renderButton(document.getElementById('signin-btn'), { theme:
63
65
 
64
66
  ## Example: Full Checkout Flow
65
67
 
68
+ Purchases are single-use: a completed purchase does not by itself grant access
69
+ again later. `has_purchased` means only "has ever bought" — buying delivers the
70
+ content directly in the `purchases.create()` response, and that response is the
71
+ only place `content_body` / `content_uri` ever appear for a purchase.
72
+
66
73
  ```ts
67
74
  const lw = Ledewire.init({ apiKey: 'your_api_key' })
68
75
 
@@ -76,21 +83,56 @@ switch (checkout_state.next_required_action) {
76
83
  const session = await lw.wallet.createPaymentSession({ amount_cents: 500 })
77
84
  // redirect to session.payment_url
78
85
  break
79
- case 'purchase':
80
- await lw.purchases.create({ content_id: 'article-123' })
81
- break
82
- case 'view_content':
83
- const { content_type, content_body, content_uri } =
84
- await lw.content.getWithAccess('article-123')
85
- if (content_type === 'markdown') {
86
- // content_body is plain text — the SDK decodes base64 automatically
87
- renderMarkdown(content_body)
88
- } else {
89
- // redirect to the gated external URI (Vimeo, PDF, etc.)
90
- window.location.href = content_uri
86
+ case 'purchase': {
87
+ // Buying is how the buyer receives the content — the delivery comes back
88
+ // directly in this response, never from a later GET. Persist it now:
89
+ // there is no route that serves the same delivery again.
90
+ const purchase = await lw.purchases.create({ content_id: 'article-123' })
91
+ await saveDeliveredContent(purchase) // your own persistence, e.g. IndexedDB
92
+
93
+ if (purchase.content_body) {
94
+ // content_body is always plain UTF-8 text on the wire — never
95
+ // base64-encoded — so no decoding step is needed. What it contains
96
+ // depends on purchase.content.content_type.
97
+ if (purchase.content.content_type === 'markdown') {
98
+ renderMarkdown(purchase.content_body)
99
+ } else if (purchase.content.content_type === 'html') {
100
+ // Inline HTML content is seller-supplied and must be sanitised
101
+ // before insertion — never assign it to innerHTML directly.
102
+ container.innerHTML = DOMPurify.sanitize(purchase.content_body)
103
+ }
104
+ } else if (purchase.content_uri) {
105
+ // content_uri points at a remote resource (video, PDF, image, or
106
+ // remote HTML). Check the scheme before navigating or linking to it —
107
+ // the URI is seller-supplied and a non-http(s) scheme should not be
108
+ // followed automatically.
109
+ const uri = new URL(purchase.content_uri)
110
+ if (['https:', 'http:'].includes(uri.protocol)) {
111
+ window.location.href = purchase.content_uri
112
+ }
91
113
  }
92
114
  break
115
+ }
116
+ }
117
+ ```
118
+
119
+ ## Example: Spend Cap & MCP API Keys
120
+
121
+ ```ts
122
+ // Every buyer starts with a default daily spend cap governing every wallet
123
+ // debit. Exceeding it throws SpendCapReachedError (402) from
124
+ // lw.purchases.create() — funding the wallet does not clear it.
125
+ const cap = await lw.user.spendCap.get()
126
+ if (cap.remaining_cents !== null && cap.remaining_cents < 500) {
127
+ console.warn(`Only ${cap.remaining_cents}c left before the cap resets at ${cap.resets_at}`)
93
128
  }
129
+ await lw.user.spendCap.update({ daily_spend_limit_cents: 2000 }) // raise to $20/day
130
+
131
+ // MCP API keys authenticate agent requests to the Ledewire MCP server. The
132
+ // secret is shown once at creation — store it immediately.
133
+ const { key, secret } = await lw.user.mcpKeys.create({ label: 'my-agent', can_search: true })
134
+ const keys = await lw.user.mcpKeys.list() // secrets never included
135
+ await lw.user.mcpKeys.revoke(keys[0].id) // to change scopes: revoke + recreate
94
136
  ```
95
137
 
96
138
  ## Example: Seller Content Discovery