@b3dotfun/sdk 0.1.65 → 0.1.66-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/dist/cjs/anyspend/react/components/AnySpend.d.ts +2 -0
  2. package/dist/cjs/anyspend/react/components/AnySpend.js +7 -16
  3. package/dist/cjs/anyspend/react/components/AnySpendCollectorClubPurchase.d.ts +6 -1
  4. package/dist/cjs/anyspend/react/components/AnySpendCollectorClubPurchase.js +151 -22
  5. package/dist/cjs/anyspend/react/components/AnySpendCustom.js +4 -50
  6. package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.d.ts +2 -0
  7. package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.js +4 -2
  8. package/dist/cjs/anyspend/react/components/AnySpendDeposit.d.ts +3 -1
  9. package/dist/cjs/anyspend/react/components/AnySpendDeposit.js +2 -2
  10. package/dist/cjs/anyspend/react/components/AnySpendWorkflowTrigger.d.ts +31 -0
  11. package/dist/cjs/anyspend/react/components/AnySpendWorkflowTrigger.js +14 -0
  12. package/dist/cjs/anyspend/react/components/QRDeposit.js +5 -13
  13. package/dist/cjs/anyspend/react/components/ccShopAbi.d.ts +113 -0
  14. package/dist/cjs/anyspend/react/components/ccShopAbi.js +63 -0
  15. package/dist/cjs/anyspend/react/components/common/CryptoPaySection.d.ts +1 -3
  16. package/dist/cjs/anyspend/react/components/common/CryptoPaySection.js +3 -3
  17. package/dist/cjs/anyspend/react/components/common/OrderTokenAmount.d.ts +1 -4
  18. package/dist/cjs/anyspend/react/components/common/OrderTokenAmount.js +3 -57
  19. package/dist/cjs/anyspend/react/components/common/PaySection.js +1 -1
  20. package/dist/cjs/anyspend/react/components/index.d.ts +2 -0
  21. package/dist/cjs/anyspend/react/components/index.js +3 -1
  22. package/dist/cjs/anyspend/react/hooks/index.d.ts +1 -0
  23. package/dist/cjs/anyspend/react/hooks/index.js +1 -0
  24. package/dist/cjs/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +1 -0
  25. package/dist/cjs/anyspend/react/hooks/useAnyspendCreateOrder.d.ts +1 -0
  26. package/dist/cjs/anyspend/react/hooks/useAnyspendCreateOrder.js +1 -0
  27. package/dist/cjs/anyspend/react/hooks/useOnOrderSuccess.d.ts +10 -0
  28. package/dist/cjs/anyspend/react/hooks/useOnOrderSuccess.js +27 -0
  29. package/dist/cjs/anyspend/services/anyspend.d.ts +2 -1
  30. package/dist/cjs/anyspend/services/anyspend.js +2 -1
  31. package/dist/cjs/anyspend/utils/chain.d.ts +1 -1
  32. package/dist/cjs/anyspend/utils/chain.js +72 -62
  33. package/dist/cjs/app.shared.js +8 -0
  34. package/dist/cjs/global-account/react/components/B3DynamicModal.js +4 -0
  35. package/dist/cjs/global-account/react/hooks/useFirstEOA.d.ts +4 -4
  36. package/dist/cjs/global-account/react/hooks/useUserQuery.js +10 -0
  37. package/dist/cjs/global-account/react/stores/useModalStore.d.ts +37 -1
  38. package/dist/cjs/global-account/react/stores/userStore.js +1 -0
  39. package/dist/esm/anyspend/react/components/AnySpend.d.ts +2 -0
  40. package/dist/esm/anyspend/react/components/AnySpend.js +7 -16
  41. package/dist/esm/anyspend/react/components/AnySpendCollectorClubPurchase.d.ts +6 -1
  42. package/dist/esm/anyspend/react/components/AnySpendCollectorClubPurchase.js +152 -23
  43. package/dist/esm/anyspend/react/components/AnySpendCustom.js +4 -17
  44. package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.d.ts +2 -0
  45. package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.js +4 -2
  46. package/dist/esm/anyspend/react/components/AnySpendDeposit.d.ts +3 -1
  47. package/dist/esm/anyspend/react/components/AnySpendDeposit.js +2 -2
  48. package/dist/esm/anyspend/react/components/AnySpendWorkflowTrigger.d.ts +31 -0
  49. package/dist/esm/anyspend/react/components/AnySpendWorkflowTrigger.js +11 -0
  50. package/dist/esm/anyspend/react/components/QRDeposit.js +6 -14
  51. package/dist/esm/anyspend/react/components/ccShopAbi.d.ts +113 -0
  52. package/dist/esm/anyspend/react/components/ccShopAbi.js +60 -0
  53. package/dist/esm/anyspend/react/components/common/CryptoPaySection.d.ts +1 -3
  54. package/dist/esm/anyspend/react/components/common/CryptoPaySection.js +3 -3
  55. package/dist/esm/anyspend/react/components/common/OrderTokenAmount.d.ts +1 -4
  56. package/dist/esm/anyspend/react/components/common/OrderTokenAmount.js +2 -56
  57. package/dist/esm/anyspend/react/components/common/PaySection.js +1 -1
  58. package/dist/esm/anyspend/react/components/index.d.ts +2 -0
  59. package/dist/esm/anyspend/react/components/index.js +1 -0
  60. package/dist/esm/anyspend/react/hooks/index.d.ts +1 -0
  61. package/dist/esm/anyspend/react/hooks/index.js +1 -0
  62. package/dist/esm/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +1 -0
  63. package/dist/esm/anyspend/react/hooks/useAnyspendCreateOrder.d.ts +1 -0
  64. package/dist/esm/anyspend/react/hooks/useAnyspendCreateOrder.js +1 -0
  65. package/dist/esm/anyspend/react/hooks/useOnOrderSuccess.d.ts +10 -0
  66. package/dist/esm/anyspend/react/hooks/useOnOrderSuccess.js +24 -0
  67. package/dist/esm/anyspend/services/anyspend.d.ts +2 -1
  68. package/dist/esm/anyspend/services/anyspend.js +2 -1
  69. package/dist/esm/anyspend/utils/chain.d.ts +1 -1
  70. package/dist/esm/anyspend/utils/chain.js +72 -62
  71. package/dist/esm/app.shared.js +8 -0
  72. package/dist/esm/global-account/react/components/B3DynamicModal.js +4 -0
  73. package/dist/esm/global-account/react/hooks/useFirstEOA.d.ts +4 -4
  74. package/dist/esm/global-account/react/hooks/useUserQuery.js +11 -1
  75. package/dist/esm/global-account/react/stores/useModalStore.d.ts +37 -1
  76. package/dist/esm/global-account/react/stores/userStore.js +1 -0
  77. package/dist/types/anyspend/react/components/AnySpend.d.ts +2 -0
  78. package/dist/types/anyspend/react/components/AnySpendCollectorClubPurchase.d.ts +6 -1
  79. package/dist/types/anyspend/react/components/AnySpendCustomExactIn.d.ts +2 -0
  80. package/dist/types/anyspend/react/components/AnySpendDeposit.d.ts +3 -1
  81. package/dist/types/anyspend/react/components/AnySpendWorkflowTrigger.d.ts +31 -0
  82. package/dist/types/anyspend/react/components/ccShopAbi.d.ts +113 -0
  83. package/dist/types/anyspend/react/components/common/CryptoPaySection.d.ts +1 -3
  84. package/dist/types/anyspend/react/components/common/OrderTokenAmount.d.ts +1 -4
  85. package/dist/types/anyspend/react/components/index.d.ts +2 -0
  86. package/dist/types/anyspend/react/hooks/index.d.ts +1 -0
  87. package/dist/types/anyspend/react/hooks/useAnyspendCreateOrder.d.ts +1 -0
  88. package/dist/types/anyspend/react/hooks/useOnOrderSuccess.d.ts +10 -0
  89. package/dist/types/anyspend/services/anyspend.d.ts +2 -1
  90. package/dist/types/anyspend/utils/chain.d.ts +1 -1
  91. package/dist/types/global-account/react/hooks/useFirstEOA.d.ts +4 -4
  92. package/dist/types/global-account/react/stores/useModalStore.d.ts +37 -1
  93. package/package.json +1 -1
  94. package/src/anyspend/README.md +14 -0
  95. package/src/anyspend/docs/checkout-sessions.md +228 -0
  96. package/src/anyspend/docs/components.md +26 -0
  97. package/src/anyspend/docs/examples.md +58 -0
  98. package/src/anyspend/docs/hooks.md +32 -0
  99. package/src/anyspend/llms.txt +185 -0
  100. package/src/anyspend/react/components/AnySpend.tsx +9 -17
  101. package/src/anyspend/react/components/AnySpendCollectorClubPurchase.tsx +206 -22
  102. package/src/anyspend/react/components/AnySpendCustom.tsx +3 -18
  103. package/src/anyspend/react/components/AnySpendCustomExactIn.tsx +5 -1
  104. package/src/anyspend/react/components/AnySpendDeposit.tsx +5 -0
  105. package/src/anyspend/react/components/AnySpendWorkflowTrigger.tsx +73 -0
  106. package/src/anyspend/react/components/QRDeposit.tsx +19 -15
  107. package/src/anyspend/react/components/ccShopAbi.ts +64 -0
  108. package/src/anyspend/react/components/common/CryptoPaySection.tsx +0 -5
  109. package/src/anyspend/react/components/common/OrderTokenAmount.tsx +1 -70
  110. package/src/anyspend/react/components/common/PaySection.tsx +0 -1
  111. package/src/anyspend/react/components/index.ts +2 -0
  112. package/src/anyspend/react/hooks/index.ts +1 -0
  113. package/src/anyspend/react/hooks/useAnyspendCreateOnrampOrder.ts +1 -0
  114. package/src/anyspend/react/hooks/useAnyspendCreateOrder.ts +2 -0
  115. package/src/anyspend/react/hooks/useOnOrderSuccess.ts +36 -0
  116. package/src/anyspend/services/anyspend.ts +3 -0
  117. package/src/anyspend/utils/chain.ts +81 -65
  118. package/src/app.shared.ts +11 -0
  119. package/src/global-account/react/components/B3DynamicModal.tsx +4 -0
  120. package/src/global-account/react/hooks/useUserQuery.ts +12 -1
  121. package/src/global-account/react/stores/useModalStore.ts +39 -2
  122. package/src/global-account/react/stores/userStore.ts +1 -0
@@ -0,0 +1,228 @@
1
+ # Checkout Sessions
2
+
3
+ Stripe-like checkout sessions for AnySpend. Sessions are decoupled from orders — create a session first, then create an order when the user is ready to pay.
4
+
5
+ ## Flow
6
+
7
+ ```
8
+ 1. Merchant creates session POST /checkout-sessions
9
+ <- { id, status: "open" }
10
+
11
+ 2. User picks payment method POST /orders { checkoutSessionId }
12
+ <- { id, globalAddress, oneClickBuyUrl }
13
+
14
+ 3. User pays Crypto: send to globalAddress
15
+ Onramp: redirect to oneClickBuyUrl
16
+
17
+ 4. Merchant polls for completion GET /checkout-sessions/:id
18
+ <- { status: "complete", order_id }
19
+ ```
20
+
21
+ ### Why Decoupled?
22
+
23
+ Session creation is instant (DB-only, no external calls). The order is created separately when the user commits to a payment method. This means:
24
+
25
+ - Payment method doesn't need to be known at session creation
26
+ - A hosted checkout page can let users choose how to pay
27
+ - Session creation never fails due to external API errors
28
+
29
+ ## Session Status Lifecycle
30
+
31
+ ```
32
+ open --> processing --> complete
33
+ |
34
+ └--> expired
35
+ ```
36
+
37
+ | Status | When |
38
+ |--------|------|
39
+ | `open` | Created, waiting for order/payment |
40
+ | `processing` | Payment received, order executing |
41
+ | `complete` | Order executed successfully |
42
+ | `expired` | TTL expired, payment failed, or manually expired |
43
+
44
+ ## API
45
+
46
+ ### `POST /checkout-sessions` — Create Session
47
+
48
+ Creates a lightweight session. No order, no external API calls.
49
+
50
+ ```json
51
+ {
52
+ "success_url": "https://merchant.com/success?session_id={SESSION_ID}",
53
+ "cancel_url": "https://merchant.com/cancel",
54
+ "metadata": { "sku": "widget-1" },
55
+ "client_reference_id": "merchant-order-456",
56
+ "expires_in": 1800
57
+ }
58
+ ```
59
+
60
+ All fields are optional. Payment config (amount, tokens, chains) lives on the order, not the session.
61
+
62
+ ### `POST /orders` — Create Order with Session Linking
63
+
64
+ Pass `checkoutSessionId` in the standard order creation request to link the order to a session.
65
+
66
+ ```json
67
+ {
68
+ "recipientAddress": "0x...",
69
+ "srcChain": 8453,
70
+ "dstChain": 8453,
71
+ "srcTokenAddress": "0x...",
72
+ "dstTokenAddress": "0x...",
73
+ "srcAmount": "1000000",
74
+ "type": "swap",
75
+ "payload": { "expectedDstAmount": "1000000" },
76
+ "checkoutSessionId": "550e8400-..."
77
+ }
78
+ ```
79
+
80
+ **Validation:**
81
+ - Session must exist (`400` if not found)
82
+ - Session must be `open` (`400` if expired/processing/complete)
83
+ - Session must not already have an order (`409 Conflict`)
84
+
85
+ ### `GET /checkout-sessions/:id` — Retrieve Session
86
+
87
+ Returns current session state. Status is synced from the underlying order on each retrieval.
88
+
89
+ | Query Param | Description |
90
+ |-------------|-------------|
91
+ | `include=order` | Embed the full order object with transactions |
92
+
93
+ ### `POST /checkout-sessions/:id/expire` — Manually Expire
94
+
95
+ Only works on sessions with status `open`.
96
+
97
+ ## Redirect URL Templates
98
+
99
+ Use template variables in `success_url` and `cancel_url`:
100
+
101
+ | Variable | Replaced with |
102
+ |----------|--------------|
103
+ | `{SESSION_ID}` | The checkout session UUID |
104
+ | `{ORDER_ID}` | Same value (alias) |
105
+
106
+ If no template variable is present, `?sessionId=<uuid>` is appended automatically.
107
+
108
+ ## SDK Integration
109
+
110
+ ### Service Methods
111
+
112
+ ```typescript
113
+ // Create a checkout session
114
+ const session = await anyspend.createCheckoutSession({
115
+ success_url: "https://mysite.com/success/{SESSION_ID}",
116
+ metadata: { sku: "widget-1" },
117
+ });
118
+
119
+ // Retrieve session status
120
+ const session = await anyspend.getCheckoutSession(sessionId);
121
+ ```
122
+
123
+ ### React Hooks
124
+
125
+ #### `useCreateCheckoutSession`
126
+
127
+ Mutation hook for creating sessions.
128
+
129
+ ```tsx
130
+ import { useCreateCheckoutSession } from "@b3dotfun/sdk/anyspend";
131
+
132
+ const { mutate: createSession, data, isPending } = useCreateCheckoutSession();
133
+ ```
134
+
135
+ #### `useCheckoutSession`
136
+
137
+ Query hook with auto-polling. Stops polling when status reaches `complete` or `expired`.
138
+
139
+ ```tsx
140
+ import { useCheckoutSession } from "@b3dotfun/sdk/anyspend";
141
+
142
+ const { data: session, isLoading } = useCheckoutSession(sessionId);
143
+ ```
144
+
145
+ ### Component `checkoutSession` Prop
146
+
147
+ The `<AnySpend>`, `<AnySpendCustom>`, and `<AnySpendCustomExactIn>` components accept an optional `checkoutSession` prop:
148
+
149
+ ```tsx
150
+ <AnySpend
151
+ defaultActiveTab="fiat"
152
+ destinationTokenAddress="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
153
+ destinationTokenChainId={8453}
154
+ recipientAddress="0x..."
155
+ checkoutSession={{
156
+ success_url: "https://myshop.com/success?session={SESSION_ID}",
157
+ cancel_url: "https://myshop.com/cancel",
158
+ metadata: { sku: "widget-1" },
159
+ }}
160
+ />
161
+ ```
162
+
163
+ When the `checkoutSession` prop is set, the component automatically creates a session before creating the order, and uses the session's `success_url` for redirects. Without the prop, existing flows are unchanged.
164
+
165
+ ## Examples
166
+
167
+ ### Crypto Payment
168
+
169
+ ```typescript
170
+ // 1. Create session
171
+ const session = await fetch("/checkout-sessions", {
172
+ method: "POST",
173
+ body: JSON.stringify({
174
+ success_url: "https://mysite.com/success/{SESSION_ID}",
175
+ metadata: { sku: "widget-1" },
176
+ }),
177
+ }).then(r => r.json());
178
+
179
+ // 2. Create order linked to session
180
+ const order = await fetch("/orders", {
181
+ method: "POST",
182
+ body: JSON.stringify({
183
+ recipientAddress: "0x...",
184
+ srcChain: 8453,
185
+ dstChain: 8453,
186
+ srcTokenAddress: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
187
+ dstTokenAddress: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
188
+ srcAmount: "1000000",
189
+ type: "swap",
190
+ payload: { expectedDstAmount: "1000000" },
191
+ checkoutSessionId: session.data.id,
192
+ }),
193
+ }).then(r => r.json());
194
+
195
+ // 3. User sends crypto to order.data.globalAddress
196
+
197
+ // 4. Poll session until complete
198
+ const poll = setInterval(async () => {
199
+ const s = await fetch(`/checkout-sessions/${session.data.id}`).then(r => r.json());
200
+ if (s.data.status === "complete") {
201
+ clearInterval(poll);
202
+ // redirect to success_url or show confirmation
203
+ }
204
+ }, 3000);
205
+ ```
206
+
207
+ ### Onramp Payment (Coinbase/Stripe)
208
+
209
+ ```typescript
210
+ // Steps 1-2 same as above, but include onramp config in order creation:
211
+ const order = await fetch("/orders", {
212
+ method: "POST",
213
+ body: JSON.stringify({
214
+ // ... same order fields ...
215
+ checkoutSessionId: session.data.id,
216
+ onramp: {
217
+ vendor: "coinbase",
218
+ payment_method: "card",
219
+ country: "US",
220
+ },
221
+ }),
222
+ }).then(r => r.json());
223
+
224
+ // Redirect user to vendor checkout page
225
+ window.location.href = order.data.oneClickBuyUrl;
226
+
227
+ // After vendor redirects back, poll GET /checkout-sessions/:id for completion
228
+ ```
@@ -207,6 +207,32 @@ const stakingCalldata = encodeFunctionData({
207
207
  />
208
208
  ```
209
209
 
210
+ ### Checkout Session Prop
211
+
212
+ The `<AnySpend>`, `<AnySpendCustom>`, and `<AnySpendCustomExactIn>` components accept an optional `checkoutSession` prop for merchant checkout flows. When set, the component creates a session before the order and uses the session's redirect URLs. See [Checkout Sessions](./checkout-sessions.md) for the full guide.
213
+
214
+ ```tsx
215
+ <AnySpend
216
+ defaultActiveTab="fiat"
217
+ destinationTokenAddress="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
218
+ destinationTokenChainId={8453}
219
+ recipientAddress="0x..."
220
+ checkoutSession={{
221
+ success_url: "https://myshop.com/success?session={SESSION_ID}",
222
+ cancel_url: "https://myshop.com/cancel",
223
+ metadata: { sku: "widget-1" },
224
+ }}
225
+ />
226
+ ```
227
+
228
+ | Prop | Type | Description |
229
+ |------|------|-------------|
230
+ | `checkoutSession.success_url` | `string` | Redirect URL on completion. Supports `{SESSION_ID}` template. |
231
+ | `checkoutSession.cancel_url` | `string` | Redirect URL on cancellation |
232
+ | `checkoutSession.metadata` | `Record<string, string>` | Custom metadata attached to the session |
233
+
234
+ ---
235
+
210
236
  ## Specialized Components
211
237
 
212
238
  ### `<AnySpendNFT>`
@@ -720,8 +720,66 @@ function PortfolioRebalancer() {
720
720
  }
721
721
  ```
722
722
 
723
+ ## 🛒 Checkout Sessions (Merchant Integration)
724
+
725
+ ### Hosted Checkout with Payment Choice
726
+
727
+ Let users choose their payment method (crypto or fiat) after session creation.
728
+
729
+ ```tsx
730
+ import { AnySpend } from "@b3dotfun/sdk/anyspend/react";
731
+
732
+ function MerchantCheckout({ sku, price }: { sku: string; price: string }) {
733
+ const [userAddress] = useWallet();
734
+
735
+ return (
736
+ <AnySpend
737
+ defaultActiveTab="fiat"
738
+ destinationTokenAddress="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
739
+ destinationTokenChainId={8453}
740
+ recipientAddress={userAddress}
741
+ checkoutSession={{
742
+ success_url: "https://myshop.com/success?session={SESSION_ID}",
743
+ cancel_url: "https://myshop.com/cancel",
744
+ metadata: { sku, price },
745
+ }}
746
+ />
747
+ );
748
+ }
749
+ ```
750
+
751
+ ### Server-Side Session with Custom Polling
752
+
753
+ ```tsx
754
+ import {
755
+ useCreateCheckoutSession,
756
+ useCheckoutSession,
757
+ } from "@b3dotfun/sdk/anyspend";
758
+
759
+ function ServerCheckout() {
760
+ const { mutate: createSession, data: session } = useCreateCheckoutSession();
761
+ const { data: sessionStatus } = useCheckoutSession(session?.data?.id);
762
+
763
+ useEffect(() => {
764
+ createSession({
765
+ success_url: "https://mysite.com/success/{SESSION_ID}",
766
+ metadata: { sku: "widget-1" },
767
+ });
768
+ }, []);
769
+
770
+ if (sessionStatus?.data?.status === "complete") {
771
+ return <div>Payment complete!</div>;
772
+ }
773
+
774
+ // Render order creation UI...
775
+ }
776
+ ```
777
+
778
+ See [Checkout Sessions](./checkout-sessions.md) for the full guide including API details and the session lifecycle.
779
+
723
780
  ## Next Steps
724
781
 
782
+ - [Checkout Sessions →](./checkout-sessions.md)
725
783
  - [Error Handling Guide →](./error-handling.md)
726
784
  - [Components Reference →](./components.md)
727
785
  - [Hooks Reference →](./hooks.md)
@@ -385,6 +385,38 @@ Get Stripe payment intent for credit card payments.
385
385
  const { clientSecret, isLoadingClientSecret } = useStripeClientSecret(orderData);
386
386
  ```
387
387
 
388
+ ### `useCreateCheckoutSession`
389
+
390
+ Create checkout sessions for merchant integrations. See [Checkout Sessions](./checkout-sessions.md) for the full guide.
391
+
392
+ ```tsx
393
+ import { useCreateCheckoutSession } from "@b3dotfun/sdk/anyspend";
394
+
395
+ const { mutate: createSession, data, isPending } = useCreateCheckoutSession();
396
+
397
+ createSession({
398
+ success_url: "https://mysite.com/success/{SESSION_ID}",
399
+ cancel_url: "https://mysite.com/cancel",
400
+ metadata: { sku: "widget-1" },
401
+ });
402
+ ```
403
+
404
+ ---
405
+
406
+ ### `useCheckoutSession`
407
+
408
+ Poll a checkout session's status. Auto-polls while the session is `open` or `processing`, and stops when `complete` or `expired`.
409
+
410
+ ```tsx
411
+ import { useCheckoutSession } from "@b3dotfun/sdk/anyspend";
412
+
413
+ const { data: session, isLoading } = useCheckoutSession(sessionId);
414
+
415
+ // session.data.status: "open" | "processing" | "complete" | "expired"
416
+ ```
417
+
418
+ ---
419
+
388
420
  ## Hook Patterns
389
421
 
390
422
  ### Error Handling Pattern
@@ -0,0 +1,185 @@
1
+ # AnySpend SDK
2
+
3
+ > Accept crypto payments and onboard users with zero friction. Users pay with any token on any chain in one seamless experience.
4
+
5
+ ## Overview
6
+
7
+ AnySpend is a payment SDK (`@b3dotfun/sdk`) for crypto payments and fiat onramps. It provides React components, hooks, and service methods for token swaps, cross-chain transactions, NFT purchases, and merchant checkout flows.
8
+
9
+ Supported networks: Ethereum, Base, B3.
10
+ Supported platforms: React Web (full), React Native (hooks + services only), Node.js (services only).
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install @b3dotfun/sdk
16
+ ```
17
+
18
+ Wrap your app with the provider:
19
+
20
+ ```tsx
21
+ import { AnySpendProvider } from "@b3dotfun/sdk/anyspend/react";
22
+ import "@b3dotfun/sdk/index.css";
23
+
24
+ function App() {
25
+ return <AnySpendProvider>{/* app */}</AnySpendProvider>;
26
+ }
27
+ ```
28
+
29
+ Requires Node.js v20.15.0+, React 18/19.
30
+
31
+ ## Components
32
+
33
+ ### AnySpend
34
+
35
+ Primary swap/onramp interface. Supports modal or page mode.
36
+
37
+ Props: `mode` ("modal"|"page"), `defaultActiveTab` ("crypto"|"fiat"), `destinationTokenAddress`, `destinationTokenChainId`, `recipientAddress`, `hideTransactionHistoryButton`, `loadOrder`, `onSuccess`, `checkoutSession`.
38
+
39
+ ```tsx
40
+ <AnySpend
41
+ mode="page"
42
+ defaultActiveTab="crypto"
43
+ recipientAddress="0x..."
44
+ onSuccess={(txHash) => console.log(txHash)}
45
+ />
46
+ ```
47
+
48
+ ### AnySpendNFTButton
49
+
50
+ One-click NFT purchase button.
51
+
52
+ Props: `nftContract` (NFTContract), `recipientAddress`, `onSuccess`.
53
+
54
+ NFTContract shape: `{ chainId, contractAddress, price (wei), priceFormatted, currency: { chainId, address, name, symbol, decimals }, name, description, imageUrl }`.
55
+
56
+ ### AnySpendCustom
57
+
58
+ Flexible component for custom smart contract interactions (staking, gaming, DeFi).
59
+
60
+ Props: `orderType` ("custom"), `dstChainId`, `dstToken`, `dstAmount`, `contractAddress`, `encodedData`, `spenderAddress`, `metadata`, `header`, `onSuccess`, `checkoutSession`.
61
+
62
+ ### AnySpendCustomExactIn
63
+
64
+ Like AnySpendCustom but for exact-input flows. Also supports `checkoutSession` prop.
65
+
66
+ ### Specialized Components
67
+
68
+ - `AnySpendNFT` — Enhanced NFT with marketplace features
69
+ - `AnySpendStakeB3` — B3 token staking
70
+ - `AnySpendBuySpin` — Gaming spin wheel purchases
71
+ - `AnySpendTournament` — Tournament entry payments
72
+
73
+ ## Hooks
74
+
75
+ ### useAnyspendQuote(quoteRequest)
76
+
77
+ Get real-time pricing. QuoteRequest: `{ srcChain, dstChain, srcTokenAddress, dstTokenAddress, type ("swap"|"custom"), tradeType ("EXACT_INPUT"|"EXACT_OUTPUT"), amount }`.
78
+
79
+ Returns: `{ anyspendQuote, isLoadingAnyspendQuote, getAnyspendQuoteError, refetchAnyspendQuote }`.
80
+
81
+ ### useAnyspendCreateOrder(options)
82
+
83
+ Create orders. Options: `{ onSuccess, onError, onSettled }`.
84
+
85
+ Returns: `{ createOrder(request), isCreatingOrder, createOrderError }`.
86
+
87
+ ### useAnyspendOrderAndTransactions(orderId)
88
+
89
+ Monitor order status and transactions in real-time.
90
+
91
+ Returns: `{ orderAndTransactions: { order, depositTxs, relayTx, executeTx, refundTxs }, isLoadingOrderAndTransactions, getOrderAndTransactionsError }`.
92
+
93
+ ### useAnyspendOrderHistory(creatorAddress, limit, offset)
94
+
95
+ Paginated order history for a user.
96
+
97
+ ### useAnyspendTokens(chainId, search)
98
+
99
+ Get available tokens for a chain.
100
+
101
+ ### useCoinbaseOnrampOptions()
102
+
103
+ Get Coinbase onramp config for fiat payments.
104
+
105
+ ### useStripeClientSecret(orderData)
106
+
107
+ Get Stripe payment intent for card payments.
108
+
109
+ ### useCreateCheckoutSession()
110
+
111
+ Mutation hook for creating checkout sessions.
112
+
113
+ Returns: `{ mutate: createSession, data, isPending }`.
114
+
115
+ ### useCheckoutSession(sessionId)
116
+
117
+ Query hook that auto-polls a checkout session. Stops on `complete` or `expired`.
118
+
119
+ Returns: `{ data: session, isLoading }`.
120
+
121
+ ## Checkout Sessions
122
+
123
+ Stripe-like checkout sessions decoupled from orders. Merchants create a session first, then create an order when the user picks a payment method.
124
+
125
+ ### Flow
126
+
127
+ 1. `POST /checkout-sessions` → Creates session (DB only, instant). Returns `{ id, status: "open" }`.
128
+ 2. `POST /orders` with `checkoutSessionId` → Creates order linked to session. Returns order with `globalAddress` or `oneClickBuyUrl`.
129
+ 3. User pays (crypto to globalAddress, or onramp redirect).
130
+ 4. `GET /checkout-sessions/:id` → Poll status. Returns `{ status: "complete", order_id }`.
131
+
132
+ ### Session Statuses
133
+
134
+ `open` → `processing` → `complete`, or `open` → `expired`.
135
+
136
+ ### API Endpoints
137
+
138
+ **POST /checkout-sessions** — Create session. All fields optional: `success_url`, `cancel_url`, `metadata`, `client_reference_id`, `expires_in`.
139
+
140
+ **POST /orders** — Standard order creation. Add `checkoutSessionId` to link. Validates: session exists (400), session is open (400), session has no order yet (409).
141
+
142
+ **GET /checkout-sessions/:id** — Retrieve session. Query `?include=order` to embed full order with transactions.
143
+
144
+ **POST /checkout-sessions/:id/expire** — Manually expire an open session.
145
+
146
+ ### Redirect URL Templates
147
+
148
+ `success_url` and `cancel_url` support `{SESSION_ID}` and `{ORDER_ID}` template variables. If no variable present, `?sessionId=<uuid>` is appended.
149
+
150
+ ### Component Integration
151
+
152
+ Pass `checkoutSession` prop to `AnySpend`, `AnySpendCustom`, or `AnySpendCustomExactIn`:
153
+
154
+ ```tsx
155
+ <AnySpend
156
+ checkoutSession={{
157
+ success_url: "https://myshop.com/success?session={SESSION_ID}",
158
+ cancel_url: "https://myshop.com/cancel",
159
+ metadata: { sku: "widget-1" },
160
+ }}
161
+ />
162
+ ```
163
+
164
+ Without the prop, all existing flows are unchanged.
165
+
166
+ ## Order Status Lifecycle
167
+
168
+ `scanning_deposit_transaction` → `obtain_token` → `sending_token_from_vault` → `relay` → `executed`
169
+
170
+ Failure states: `obtain_failed`, `expired`, `refunding`, `refunded`, `failure`.
171
+
172
+ For Stripe: `waiting_stripe_payment` precedes processing.
173
+
174
+ ## Error Codes
175
+
176
+ Payment: `INSUFFICIENT_BALANCE`, `INVALID_TOKEN_ADDRESS`, `MINIMUM_AMOUNT_NOT_MET`, `MAXIMUM_AMOUNT_EXCEEDED`.
177
+ Network: `SLIPPAGE`, `NETWORK_ERROR`, `QUOTE_EXPIRED`, `CHAIN_NOT_SUPPORTED`.
178
+ Contract: `CONTRACT_CALL_FAILED`, `INSUFFICIENT_GAS`, `NONCE_TOO_LOW`, `TRANSACTION_REVERTED`.
179
+
180
+ ## Links
181
+
182
+ - Docs: docs/installation.md, docs/components.md, docs/hooks.md, docs/examples.md, docs/checkout-sessions.md, docs/error-handling.md
183
+ - Live demo: https://anyspend.com
184
+ - Discord: https://discord.gg/b3dotfun
185
+ - Issues: https://github.com/b3-fun/b3/issues
@@ -52,6 +52,7 @@ import { useAutoSelectCryptoPaymentMethod } from "../hooks/useAutoSelectCryptoPa
52
52
  import { useConnectedWalletDisplay } from "../hooks/useConnectedWalletDisplay";
53
53
  import { useCryptoPaymentMethodState } from "../hooks/useCryptoPaymentMethodState";
54
54
  import { useDirectTransfer } from "../hooks/useDirectTransfer";
55
+ import { useOnOrderSuccess } from "../hooks/useOnOrderSuccess";
55
56
  import { useRecipientAddressState } from "../hooks/useRecipientAddressState";
56
57
  import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFingerprintWrapper";
57
58
  import { CryptoPaymentMethod, CryptoPaymentMethodType } from "./common/CryptoPaymentMethod";
@@ -126,6 +127,8 @@ export function AnySpend(props: {
126
127
  allowDirectTransfer?: boolean;
127
128
  /** Fixed destination token amount (in wei/smallest unit). When provided, user cannot change the amount. */
128
129
  destinationTokenAmount?: string;
130
+ /** Opaque metadata passed to the order for callbacks (e.g., workflow form data) */
131
+ callbackMetadata?: Record<string, unknown>;
129
132
  }) {
130
133
  const fingerprintConfig = getFingerprintConfig();
131
134
 
@@ -158,6 +161,7 @@ function AnySpendInner({
158
161
  classes,
159
162
  allowDirectTransfer = false,
160
163
  destinationTokenAmount,
164
+ callbackMetadata,
161
165
  }: {
162
166
  sourceChainId?: number;
163
167
  destinationTokenAddress?: string;
@@ -179,6 +183,7 @@ function AnySpendInner({
179
183
  classes?: AnySpendClasses;
180
184
  allowDirectTransfer?: boolean;
181
185
  destinationTokenAmount?: string;
186
+ callbackMetadata?: Record<string, unknown>;
182
187
  }) {
183
188
  const searchParams = useSearchParamsSSR();
184
189
  const router = useRouter();
@@ -208,9 +213,6 @@ function AnySpendInner({
208
213
  toAmount?: string;
209
214
  } | null>(null);
210
215
 
211
- // Track if onSuccess has been called for the current order
212
- const onSuccessCalled = useRef(false);
213
-
214
216
  // Track animation direction for TransitionPanel
215
217
  const animationDirection = useRef<"forward" | "back" | null>(null);
216
218
  // Track previous panel for proper back navigation
@@ -704,19 +706,8 @@ function AnySpendInner({
704
706
  }
705
707
  }, [anyspendQuote, isSrcInputDirty, destinationTokenAmount]);
706
708
 
707
- useEffect(() => {
708
- if (oat?.data?.order.status === "executed" && !onSuccessCalled.current) {
709
- console.log("Calling onSuccess");
710
- const txHash = oat?.data?.executeTx?.txHash;
711
- onSuccess?.(txHash);
712
- onSuccessCalled.current = true;
713
- }
714
- }, [oat?.data?.order.status, oat?.data?.executeTx?.txHash, onSuccess]);
715
-
716
- // Reset flag when orderId changes
717
- useEffect(() => {
718
- onSuccessCalled.current = false;
719
- }, [orderId]);
709
+ // Call onSuccess when order is executed
710
+ useOnOrderSuccess({ orderData: oat, orderId, onSuccess });
720
711
 
721
712
  const { createOrder, isCreatingOrder } = useAnyspendCreateOrder({
722
713
  onSuccess: data => {
@@ -979,6 +970,7 @@ function AnySpendInner({
979
970
  srcAmount: srcAmountBigInt.toString(),
980
971
  expectedDstAmount: anyspendQuote?.data?.currencyOut?.amount || "0",
981
972
  creatorAddress: globalAddress,
973
+ callbackMetadata,
982
974
  });
983
975
  } catch (err: any) {
984
976
  console.error(err);
@@ -1056,6 +1048,7 @@ function AnySpendInner({
1056
1048
  },
1057
1049
  expectedDstAmount: anyspendQuote?.data?.currencyOut?.amount?.toString() || "0",
1058
1050
  creatorAddress: globalAddress,
1051
+ callbackMetadata,
1059
1052
  });
1060
1053
  } catch (err: any) {
1061
1054
  console.error(err);
@@ -1227,7 +1220,6 @@ function AnySpendInner({
1227
1220
  anyspendQuote={anyspendQuote}
1228
1221
  onTokenSelect={onTokenSelect}
1229
1222
  onShowFeeDetail={() => navigateToPanel(PanelView.FEE_DETAIL, "forward")}
1230
- skipAutoMaxOnTokenChange={!!destinationTokenAmount}
1231
1223
  />
1232
1224
  ) : (
1233
1225
  <motion.div