@bacano/sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AUTHENTICATION.md +365 -0
- package/LICENSE +21 -0
- package/README.md +1357 -0
- package/client.d.ts +96 -0
- package/errors.d.ts +24 -0
- package/graphql.cjs +2057 -0
- package/graphql.d.ts +17 -0
- package/graphql.js +2034 -0
- package/index.cjs +4956 -0
- package/index.d.ts +8 -0
- package/index.js +4933 -0
- package/media.d.ts +9 -0
- package/modules/auth.d.ts +9 -0
- package/modules/availability.d.ts +6 -0
- package/modules/cart-session.d.ts +13 -0
- package/modules/cart.d.ts +16 -0
- package/modules/catalog.d.ts +24 -0
- package/modules/checkout.d.ts +42 -0
- package/modules/content.d.ts +9 -0
- package/modules/orders.d.ts +9 -0
- package/modules/profile.d.ts +8 -0
- package/modules/website.d.ts +3 -0
- package/package.json +52 -0
- package/react/context.d.ts +7 -0
- package/react/hooks.d.ts +74 -0
- package/react/provider.d.ts +7 -0
- package/react.cjs +5232 -0
- package/react.d.ts +23 -0
- package/react.js +5209 -0
- package/transport/documents.d.ts +83 -0
- package/transport/graphql.d.ts +7 -0
- package/types.d.ts +991 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
# Storefront Buyer Authentication
|
|
2
|
+
|
|
3
|
+
This document is the canonical authentication contract for storefronts built
|
|
4
|
+
with `@bacano/sdk`. It is shipped with the package so a developer or coding
|
|
5
|
+
agent can integrate buyer accounts without access to the Bacano monorepo.
|
|
6
|
+
|
|
7
|
+
## Ownership Boundary
|
|
8
|
+
|
|
9
|
+
Each Bacano website has its own Clerk application. This gives the same email
|
|
10
|
+
address an independent account and password in different storefronts.
|
|
11
|
+
|
|
12
|
+
Clerk owns:
|
|
13
|
+
|
|
14
|
+
- sign-up and sign-in;
|
|
15
|
+
- passwords and password recovery;
|
|
16
|
+
- email verification;
|
|
17
|
+
- session refresh, MFA, OAuth, and passkeys when enabled.
|
|
18
|
+
|
|
19
|
+
Bacano owns:
|
|
20
|
+
|
|
21
|
+
- mapping the verified Clerk user to a customer of the exact website;
|
|
22
|
+
- guest and authenticated carts;
|
|
23
|
+
- checkout and order ownership;
|
|
24
|
+
- customer profile and web order history;
|
|
25
|
+
- company and website isolation.
|
|
26
|
+
|
|
27
|
+
The storefront owns presentation only. It may use Clerk components or a custom
|
|
28
|
+
UI backed by Clerk's official SDK. Passwords and verification codes must go
|
|
29
|
+
directly from the browser to Clerk. They must never be sent to Bacano or
|
|
30
|
+
`@bacano/sdk`.
|
|
31
|
+
|
|
32
|
+
## Security Invariants
|
|
33
|
+
|
|
34
|
+
- Never put a Clerk secret key, Bacano admin secret, or internal API key in the
|
|
35
|
+
browser.
|
|
36
|
+
- Never store the Clerk access token in `localStorage`. Clerk owns its session.
|
|
37
|
+
- Give Bacano a token provider function, not a captured token.
|
|
38
|
+
- Never send `clientId`, `companyId`, or a claimed buyer identity from checkout.
|
|
39
|
+
Bacano derives identity from the verified token.
|
|
40
|
+
- Never associate a guest purchase with an account only because emails match.
|
|
41
|
+
- Never query Hasura directly from a storefront.
|
|
42
|
+
- Always configure and send the exact Bacano `websiteSlug`.
|
|
43
|
+
- Always preserve the real storefront `Origin`, including protocol and port.
|
|
44
|
+
|
|
45
|
+
Bacano validates the explicit website slug, request `Origin`, token signature,
|
|
46
|
+
Clerk issuer, and the JWT `azp` authorized party before producing tenant-scoped
|
|
47
|
+
Hasura claims. The slug selects the website; Origin proves that the caller is
|
|
48
|
+
allowed to use it.
|
|
49
|
+
|
|
50
|
+
## Public Website Bootstrap
|
|
51
|
+
|
|
52
|
+
The Clerk publishable key is website-specific and safe for browser use. Load it
|
|
53
|
+
at runtime instead of hardcoding one global Clerk application:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { getWebsitePublicConfig } from "@bacano/sdk";
|
|
57
|
+
|
|
58
|
+
const website = await getWebsitePublicConfig({
|
|
59
|
+
apiUrl: process.env.NEXT_PUBLIC_BACANO_API_URL!,
|
|
60
|
+
websiteSlug: process.env.NEXT_PUBLIC_BACANO_WEBSITE_SLUG!,
|
|
61
|
+
// Required for server-side calls. Browsers provide their own Origin.
|
|
62
|
+
origin: "https://store.example.com",
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
console.log(website.slug);
|
|
66
|
+
console.log(website.buyerAccountsEnabled);
|
|
67
|
+
console.log(website.clerkPublishableKey);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The response exposes only browser-safe website data. Clerk secret keys and
|
|
71
|
+
JWKS configuration never leave Bacano.
|
|
72
|
+
|
|
73
|
+
If `buyerAccountsEnabled` is false or `clerkPublishableKey` is null, keep the
|
|
74
|
+
storefront in guest mode. Catalog, cart, and guest checkout remain available.
|
|
75
|
+
|
|
76
|
+
## Next.js And React
|
|
77
|
+
|
|
78
|
+
Install the official Clerk integration used by the storefront framework. For
|
|
79
|
+
Next.js:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pnpm add @clerk/nextjs
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Mount `BacanoProvider` inside the website-specific `ClerkProvider`:
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
"use client";
|
|
89
|
+
|
|
90
|
+
import { ClerkProvider, useAuth } from "@clerk/nextjs";
|
|
91
|
+
import { BacanoProvider } from "@bacano/sdk/react";
|
|
92
|
+
import type { ReactNode } from "react";
|
|
93
|
+
|
|
94
|
+
function StorefrontBacanoProvider({ children }: { children: ReactNode }) {
|
|
95
|
+
const { getToken } = useAuth();
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<BacanoProvider
|
|
99
|
+
config={{
|
|
100
|
+
apiUrl: process.env.NEXT_PUBLIC_BACANO_API_URL!,
|
|
101
|
+
websiteSlug: process.env.NEXT_PUBLIC_BACANO_WEBSITE_SLUG!,
|
|
102
|
+
getAccessToken: () => getToken(),
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
{children}
|
|
106
|
+
</BacanoProvider>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function StorefrontProviders({
|
|
111
|
+
publishableKey,
|
|
112
|
+
children,
|
|
113
|
+
}: {
|
|
114
|
+
publishableKey: string;
|
|
115
|
+
children: ReactNode;
|
|
116
|
+
}) {
|
|
117
|
+
return (
|
|
118
|
+
<ClerkProvider publishableKey={publishableKey}>
|
|
119
|
+
<StorefrontBacanoProvider>{children}</StorefrontBacanoProvider>
|
|
120
|
+
</ClerkProvider>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Do not call `getToken()` once during initialization and retain its result. The
|
|
126
|
+
SDK calls `getAccessToken` for every request so Clerk can refresh or replace
|
|
127
|
+
the session token.
|
|
128
|
+
|
|
129
|
+
## Framework-Agnostic Client
|
|
130
|
+
|
|
131
|
+
The same contract works without React:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
import { createBacanoClient } from "@bacano/sdk";
|
|
135
|
+
|
|
136
|
+
const client = createBacanoClient({
|
|
137
|
+
apiUrl: "https://api.bacanoerp.com",
|
|
138
|
+
websiteSlug: "my-store",
|
|
139
|
+
getAccessToken: async () => clerkSession?.getToken() ?? null,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
await client.init();
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The SDK intentionally does not expose `login`, `register`, `logout`, or
|
|
146
|
+
password methods. Use the official Clerk SDK for those operations.
|
|
147
|
+
|
|
148
|
+
## Guest And Authenticated Behavior
|
|
149
|
+
|
|
150
|
+
Guest buyer:
|
|
151
|
+
|
|
152
|
+
- can browse, use the cart, and submit checkout;
|
|
153
|
+
- does not receive an account silently;
|
|
154
|
+
- can use the public order lookup token returned by checkout.
|
|
155
|
+
|
|
156
|
+
Authenticated buyer:
|
|
157
|
+
|
|
158
|
+
- uses the same cart and checkout methods;
|
|
159
|
+
- is associated by Bacano from verified Hasura session variables;
|
|
160
|
+
- can read and update their Bacano customer profile;
|
|
161
|
+
- can read only their own `WEB` orders for the exact website.
|
|
162
|
+
|
|
163
|
+
Checkout code is identical in both modes:
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
const result = await client.checkout.submit({
|
|
167
|
+
contact: {
|
|
168
|
+
firstName: "Maria",
|
|
169
|
+
lastName: "Perez",
|
|
170
|
+
email: "maria@example.com",
|
|
171
|
+
cellphone: "+573001234567",
|
|
172
|
+
},
|
|
173
|
+
fulfillmentFamily: "SHIPPING",
|
|
174
|
+
address,
|
|
175
|
+
shippingQuoteId,
|
|
176
|
+
paymentMethodId,
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Do not add a `clientId` to the checkout payload. If Clerk has an active
|
|
181
|
+
session, the SDK sends its token automatically. Otherwise checkout remains a
|
|
182
|
+
guest checkout.
|
|
183
|
+
|
|
184
|
+
If checkout collects a mobile/WhatsApp number that is not yet in the Bacano
|
|
185
|
+
profile, synchronize it explicitly through the authenticated profile API:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
await client.profile.update({ cellphone: contact.phone });
|
|
189
|
+
await client.checkout.submit({
|
|
190
|
+
contact,
|
|
191
|
+
fulfillmentFamily: "SHIPPING",
|
|
192
|
+
address,
|
|
193
|
+
shippingQuoteId,
|
|
194
|
+
paymentMethodId,
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
This is an explicit commercial-profile update, not identity matching. Checkout
|
|
199
|
+
never accepts `clientId`; Bacano associates the order from the verified Clerk
|
|
200
|
+
token. Guest checkout continues without this call or an access token.
|
|
201
|
+
|
|
202
|
+
## Deferred Account Activation From Checkout
|
|
203
|
+
|
|
204
|
+
A guest may explicitly request an account without interrupting payment or
|
|
205
|
+
email verification during checkout:
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
const result = await client.checkout.submit({
|
|
209
|
+
contact,
|
|
210
|
+
fulfillmentFamily: "SHIPPING",
|
|
211
|
+
address,
|
|
212
|
+
shippingQuoteId,
|
|
213
|
+
paymentMethodId,
|
|
214
|
+
buyerAccountOptIn: true,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
console.log(result.buyerAccountInvitationRequested);
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
`buyerAccountOptIn` is only a consent signal. After Bacano validates and
|
|
221
|
+
persists a real guest order, it queues the Clerk application invitation and
|
|
222
|
+
returns the checkout response without waiting for Clerk. The invitation does
|
|
223
|
+
not depend on payment approval: pending bank transfers, cash on delivery and
|
|
224
|
+
payment-session orders are eligible as soon as the order exists. Invalid
|
|
225
|
+
checkout attempts that never create an order do not send invitations. Failed
|
|
226
|
+
invitation delivery never rolls back or changes the order.
|
|
227
|
+
|
|
228
|
+
After Clerk verifies the buyer and accepts the activation reference, Bacano
|
|
229
|
+
atomically assigns the order to that website-scoped buyer and fills only
|
|
230
|
+
missing commercial profile fields from the persisted checkout snapshot. This
|
|
231
|
+
includes name, mobile and primary shipping address. Existing profile data is
|
|
232
|
+
preserved, and the authenticated email always comes from Clerk rather than the
|
|
233
|
+
checkout payload.
|
|
234
|
+
|
|
235
|
+
The storefront must show this option only after the buyer explicitly checks a
|
|
236
|
+
label such as "Crear mi cuenta para consultar este pedido después". Do not
|
|
237
|
+
preselect it. If the buyer is already authenticated, Bacano ignores the option
|
|
238
|
+
and associates the order from the verified Clerk token instead.
|
|
239
|
+
|
|
240
|
+
The invitation redirects to the website's configured activation path (default
|
|
241
|
+
`/crear-cuenta`) with Clerk's `__clerk_ticket` query parameter. That page must
|
|
242
|
+
complete Clerk's official invitation flow. After Clerk establishes a session,
|
|
243
|
+
make an authenticated SDK request such as `client.profile.get()`. Bacano then
|
|
244
|
+
links the order using the backend-issued activation reference, the exact
|
|
245
|
+
website and company, and Clerk's verified primary email. The storefront never
|
|
246
|
+
sends an order claim, email match, invitation id, password, code, or secret.
|
|
247
|
+
|
|
248
|
+
`buyerAccountInvitationRequested` confirms that Bacano durably accepted the
|
|
249
|
+
request; it does not mean the email has already been delivered, that the buyer
|
|
250
|
+
has activated the account, or that payment was approved.
|
|
251
|
+
|
|
252
|
+
## Profile And Orders
|
|
253
|
+
|
|
254
|
+
These methods require an authenticated Clerk session:
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
const profile = await client.profile.get();
|
|
258
|
+
const addresses = await client.profile.getAddresses();
|
|
259
|
+
|
|
260
|
+
await client.profile.update({
|
|
261
|
+
name: "Ada",
|
|
262
|
+
lastname: "Lovelace",
|
|
263
|
+
cellphone: "+573001234567",
|
|
264
|
+
address: "Carrera 10 # 20-30",
|
|
265
|
+
addressComplement: "Apto 401",
|
|
266
|
+
countryCode: "CO",
|
|
267
|
+
departmentId: "68",
|
|
268
|
+
municipalityId: "68001",
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const orders = await client.orders.list({ limit: 20, offset: 0 });
|
|
272
|
+
const order = await client.orders.get(orders[0].id);
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Account pages must handle an expired or missing Clerk session by asking the
|
|
276
|
+
buyer to sign in again. They must not fall back to a public GraphQL query.
|
|
277
|
+
|
|
278
|
+
`profile.email` is the verified primary email synchronized by Bacano directly
|
|
279
|
+
from Clerk. It is read-only: storefronts must not send email, passwords,
|
|
280
|
+
verification codes, tokens, or any other identity secret to `profile.update()`.
|
|
281
|
+
|
|
282
|
+
Use `cellphone` as the canonical mobile/WhatsApp number in E.164 format
|
|
283
|
+
(`+573001234567`). `phone` is reserved for an optional secondary or
|
|
284
|
+
national-format telephone number. A WhatsApp opt-in, if added later, must be a
|
|
285
|
+
separate consent field rather than another alias for the same number.
|
|
286
|
+
|
|
287
|
+
Calling `profile.update()` repeatedly is safe: it updates the authenticated
|
|
288
|
+
buyer's existing party and never creates or matches customers by email.
|
|
289
|
+
|
|
290
|
+
`profile.getAddresses()` resolves the customer exclusively from the verified
|
|
291
|
+
Clerk token and current website. It returns only checkout-ready commercial
|
|
292
|
+
(`PRIMARY`) and saved delivery (`SHIPPING`) addresses whose department and
|
|
293
|
+
municipality are canonical. Incomplete legacy addresses are omitted so the
|
|
294
|
+
storefront requests location again instead of matching it by text. Use
|
|
295
|
+
`departmentId`/`municipalityId` directly when pre-filling checkout and quoting
|
|
296
|
+
shipping; do not send a `clientId`.
|
|
297
|
+
|
|
298
|
+
## Server Rendering And Static Sites
|
|
299
|
+
|
|
300
|
+
Public catalog and content requests may run without an access token.
|
|
301
|
+
|
|
302
|
+
For an authenticated server-side request, use Clerk's server auth API to
|
|
303
|
+
provide a token for that request and forward the storefront's exact `Origin`.
|
|
304
|
+
Never reuse one buyer token across requests or cache authenticated responses
|
|
305
|
+
between users.
|
|
306
|
+
|
|
307
|
+
Static storefronts should initialize Clerk in the browser from the public
|
|
308
|
+
website bootstrap. Do not bake Clerk secret keys or buyer tokens into static
|
|
309
|
+
output.
|
|
310
|
+
|
|
311
|
+
## Required Configuration
|
|
312
|
+
|
|
313
|
+
Before testing a website:
|
|
314
|
+
|
|
315
|
+
1. Bacano website auth provisioning must be `READY`.
|
|
316
|
+
2. The website must contain its Clerk publishable key and JWKS URL.
|
|
317
|
+
3. The storefront origin must be configured in Bacano.
|
|
318
|
+
4. The same origin must be allowed by Clerk.
|
|
319
|
+
5. Redirect URLs for sign-in, sign-up, verification, and recovery must match
|
|
320
|
+
the storefront environment.
|
|
321
|
+
6. Local origins must include the exact port.
|
|
322
|
+
|
|
323
|
+
## Acceptance Checklist
|
|
324
|
+
|
|
325
|
+
Guest mode:
|
|
326
|
+
|
|
327
|
+
- catalog and content load;
|
|
328
|
+
- cart persists;
|
|
329
|
+
- checkout succeeds without creating an account.
|
|
330
|
+
|
|
331
|
+
Account mode:
|
|
332
|
+
|
|
333
|
+
- sign-up and email verification succeed through Clerk;
|
|
334
|
+
- sign-in, refresh, sign-out, and password recovery work;
|
|
335
|
+
- authenticated checkout appears in `orders.list()`;
|
|
336
|
+
- profile reads and updates work.
|
|
337
|
+
|
|
338
|
+
Isolation:
|
|
339
|
+
|
|
340
|
+
- a token issued for another Clerk application is rejected;
|
|
341
|
+
- a token with a different `azp` origin is rejected;
|
|
342
|
+
- buyers cannot read another website's orders;
|
|
343
|
+
- POS and physical-store orders never appear in the storefront account.
|
|
344
|
+
|
|
345
|
+
Regression:
|
|
346
|
+
|
|
347
|
+
- guest checkout still works after Clerk is enabled;
|
|
348
|
+
- deferred account opt-in does not delay or fail checkout;
|
|
349
|
+
- an accepted invitation exposes its order only after an authenticated SDK
|
|
350
|
+
request completes the secure claim;
|
|
351
|
+
- payment-session flows still work;
|
|
352
|
+
- catalog, content, media, and public order lookup remain public where
|
|
353
|
+
intended.
|
|
354
|
+
|
|
355
|
+
## Deliberately Unsupported
|
|
356
|
+
|
|
357
|
+
- Silent account creation from checkout contact fields.
|
|
358
|
+
- Claiming old guest orders by email match alone.
|
|
359
|
+
- Sharing one Clerk account or password across independent websites.
|
|
360
|
+
- Storefront-managed JWT persistence.
|
|
361
|
+
- Direct Hasura access from the storefront.
|
|
362
|
+
|
|
363
|
+
Only orders carrying an explicit deferred-account opt-in can be claimed later,
|
|
364
|
+
and Bacano performs that claim from backend-issued invitation metadata plus a
|
|
365
|
+
verified Clerk identity.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pulpo Labs
|
|
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.
|