@cedros/pay-react 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 +21 -0
- package/README.md +1731 -0
- package/dist/CedrosContext-B3iCqN6e.js +11 -0
- package/dist/CedrosContext-vX9uqZKp.mjs +1796 -0
- package/dist/ar-LVoQZTFI.js +1 -0
- package/dist/ar-w27mU-4x.mjs +19 -0
- package/dist/bn-BR5Cv1T4.js +1 -0
- package/dist/bn-Ba_k3Kex.mjs +19 -0
- package/dist/components/CedrosPay.d.ts +54 -0
- package/dist/components/CedrosPay.d.ts.map +1 -0
- package/dist/components/CryptoButton.d.ts +26 -0
- package/dist/components/CryptoButton.d.ts.map +1 -0
- package/dist/components/PaymentModal.d.ts +28 -0
- package/dist/components/PaymentModal.d.ts.map +1 -0
- package/dist/components/ProductPrice.d.ts +56 -0
- package/dist/components/ProductPrice.d.ts.map +1 -0
- package/dist/components/PurchaseButton.d.ts +52 -0
- package/dist/components/PurchaseButton.d.ts.map +1 -0
- package/dist/components/StripeButton.d.ts +27 -0
- package/dist/components/StripeButton.d.ts.map +1 -0
- package/dist/context/CedrosContext.d.ts +46 -0
- package/dist/context/CedrosContext.d.ts.map +1 -0
- package/dist/context/ThemeContext.d.ts +30 -0
- package/dist/context/ThemeContext.d.ts.map +1 -0
- package/dist/context/index.d.ts +3 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/crypto-only.d.ts +33 -0
- package/dist/crypto-only.d.ts.map +1 -0
- package/dist/crypto-only.js +1 -0
- package/dist/crypto-only.mjs +35 -0
- package/dist/de-CoZiPFN7.mjs +19 -0
- package/dist/de-pQxy-oD1.js +1 -0
- package/dist/en-CSsJl3nf.mjs +19 -0
- package/dist/en-D-uY3ltT.js +1 -0
- package/dist/es-BWGIBp2f.mjs +19 -0
- package/dist/es-D24cg8dD.js +1 -0
- package/dist/fil-BOBft9G-.js +1 -0
- package/dist/fil-Czo27xmj.mjs +19 -0
- package/dist/fr-Ct9ub8Fa.js +1 -0
- package/dist/fr-DQ-2ThBv.mjs +19 -0
- package/dist/he-DpV1WnBQ.mjs +19 -0
- package/dist/he-DtQqRKRq.js +1 -0
- package/dist/hooks/usePaymentMode.d.ts +39 -0
- package/dist/hooks/usePaymentMode.d.ts.map +1 -0
- package/dist/hooks/useRefundVerification.d.ts +30 -0
- package/dist/hooks/useRefundVerification.d.ts.map +1 -0
- package/dist/hooks/useStripeCheckout.d.ts +20 -0
- package/dist/hooks/useStripeCheckout.d.ts.map +1 -0
- package/dist/hooks/useX402Payment.d.ts +24 -0
- package/dist/hooks/useX402Payment.d.ts.map +1 -0
- package/dist/i18n/index.d.ts +107 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/useTranslation.d.ts +55 -0
- package/dist/i18n/useTranslation.d.ts.map +1 -0
- package/dist/id-BJMqsu19.mjs +19 -0
- package/dist/id-CiM2mL7C.js +1 -0
- package/dist/in-BxgxKLQH.mjs +19 -0
- package/dist/in-Bzcjmxcc.js +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.mjs +626 -0
- package/dist/it-Blb_pIJl.js +1 -0
- package/dist/it-DZFFPALf.mjs +19 -0
- package/dist/jp-9NHyIuwY.js +1 -0
- package/dist/jp-ZExTrlHK.mjs +19 -0
- package/dist/kr-DHX3i4Ht.mjs +19 -0
- package/dist/kr-DvzJ-0yX.js +1 -0
- package/dist/managers/ManagerCache.d.ts +49 -0
- package/dist/managers/ManagerCache.d.ts.map +1 -0
- package/dist/managers/RouteDiscoveryManager.d.ts +72 -0
- package/dist/managers/RouteDiscoveryManager.d.ts.map +1 -0
- package/dist/managers/StripeManager.d.ts +110 -0
- package/dist/managers/StripeManager.d.ts.map +1 -0
- package/dist/managers/WalletManager.d.ts +150 -0
- package/dist/managers/WalletManager.d.ts.map +1 -0
- package/dist/managers/X402Manager.d.ts +176 -0
- package/dist/managers/X402Manager.d.ts.map +1 -0
- package/dist/ms-BOAu5pUB.js +1 -0
- package/dist/ms-Cv1fdIi2.mjs +19 -0
- package/dist/nl-BmGonsKb.mjs +19 -0
- package/dist/nl-WHh_DfO8.js +1 -0
- package/dist/pa-B7kIhZCF.js +1 -0
- package/dist/pa-BfwcJIar.mjs +19 -0
- package/dist/pay-react.css +1 -0
- package/dist/pl-DE5IB9xv.mjs +19 -0
- package/dist/pl-H0hBKdvF.js +1 -0
- package/dist/pt-CLzkqDzf.mjs +19 -0
- package/dist/pt-DwGrViQ3.js +1 -0
- package/dist/ru-CB2m0UDT.js +1 -0
- package/dist/ru-DM6-oUR0.mjs +19 -0
- package/dist/stripe-only.d.ts +31 -0
- package/dist/stripe-only.d.ts.map +1 -0
- package/dist/stripe-only.js +1 -0
- package/dist/stripe-only.mjs +33 -0
- package/dist/styles-D3XGpsqb.js +1 -0
- package/dist/styles-zgmHs6Hs.mjs +1601 -0
- package/dist/ta-A5HnrGb5.mjs +19 -0
- package/dist/ta-CAS197uN.js +1 -0
- package/dist/telemetry.d.ts +27 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +1 -0
- package/dist/telemetry.mjs +127 -0
- package/dist/testing/__tests__/providers.test.d.ts +10 -0
- package/dist/testing/__tests__/providers.test.d.ts.map +1 -0
- package/dist/testing/helpers.d.ts +242 -0
- package/dist/testing/helpers.d.ts.map +1 -0
- package/dist/testing/index.d.ts +12 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +1 -0
- package/dist/testing/index.mjs +374 -0
- package/dist/testing/mocks.d.ts +225 -0
- package/dist/testing/mocks.d.ts.map +1 -0
- package/dist/testing/providers.d.ts +89 -0
- package/dist/testing/providers.d.ts.map +1 -0
- package/dist/th-3fbB3Ytp.mjs +19 -0
- package/dist/th-Cpz2cFcg.js +1 -0
- package/dist/tr-BrgfFFdq.mjs +19 -0
- package/dist/tr-hQrEFk86.js +1 -0
- package/dist/types/componentOptions.d.ts +138 -0
- package/dist/types/componentOptions.d.ts.map +1 -0
- package/dist/types/errors.d.ts +213 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/index.d.ts +248 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/uk-0hFun_g_.mjs +19 -0
- package/dist/uk-DrK2Sv8C.js +1 -0
- package/dist/ur-CaOjJXai.mjs +19 -0
- package/dist/ur-D5-7mN9a.js +1 -0
- package/dist/utils/__tests__/cspHelper.test.d.ts +9 -0
- package/dist/utils/__tests__/cspHelper.test.d.ts.map +1 -0
- package/dist/utils/__tests__/fetchWithTimeout.test.d.ts +11 -0
- package/dist/utils/__tests__/fetchWithTimeout.test.d.ts.map +1 -0
- package/dist/utils/cartHelpers.d.ts +105 -0
- package/dist/utils/cartHelpers.d.ts.map +1 -0
- package/dist/utils/circuitBreaker.d.ts +112 -0
- package/dist/utils/circuitBreaker.d.ts.map +1 -0
- package/dist/utils/couponHelpers.d.ts +50 -0
- package/dist/utils/couponHelpers.d.ts.map +1 -0
- package/dist/utils/cspHelper.d.ts +162 -0
- package/dist/utils/cspHelper.d.ts.map +1 -0
- package/dist/utils/deprecation.d.ts +128 -0
- package/dist/utils/deprecation.d.ts.map +1 -0
- package/dist/utils/errorHandling.d.ts +30 -0
- package/dist/utils/errorHandling.d.ts.map +1 -0
- package/dist/utils/errorMessages.d.ts +47 -0
- package/dist/utils/errorMessages.d.ts.map +1 -0
- package/dist/utils/errorParser.d.ts +37 -0
- package/dist/utils/errorParser.d.ts.map +1 -0
- package/dist/utils/eventEmitter.d.ts +119 -0
- package/dist/utils/eventEmitter.d.ts.map +1 -0
- package/dist/utils/exponentialBackoff.d.ts +104 -0
- package/dist/utils/exponentialBackoff.d.ts.map +1 -0
- package/dist/utils/fetchWithTimeout.d.ts +13 -0
- package/dist/utils/fetchWithTimeout.d.ts.map +1 -0
- package/dist/utils/index.d.ts +15 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/logger.d.ts +76 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/modalStyles.d.ts +13 -0
- package/dist/utils/modalStyles.d.ts.map +1 -0
- package/dist/utils/rateLimiter.d.ts +88 -0
- package/dist/utils/rateLimiter.d.ts.map +1 -0
- package/dist/utils/requestDeduplication.d.ts +124 -0
- package/dist/utils/requestDeduplication.d.ts.map +1 -0
- package/dist/utils/securityValidation.d.ts +75 -0
- package/dist/utils/securityValidation.d.ts.map +1 -0
- package/dist/utils/solanaCheck.d.ts +10 -0
- package/dist/utils/solanaCheck.d.ts.map +1 -0
- package/dist/utils/telemetry.d.ts +277 -0
- package/dist/utils/telemetry.d.ts.map +1 -0
- package/dist/utils/tokenMintValidator.d.ts +77 -0
- package/dist/utils/tokenMintValidator.d.ts.map +1 -0
- package/dist/utils/uuid.d.ts +13 -0
- package/dist/utils/uuid.d.ts.map +1 -0
- package/dist/utils/validateConfig.d.ts +13 -0
- package/dist/utils/validateConfig.d.ts.map +1 -0
- package/dist/utils/walletDetection.d.ts +6 -0
- package/dist/utils/walletDetection.d.ts.map +1 -0
- package/dist/utils/walletPool.d.ts +57 -0
- package/dist/utils/walletPool.d.ts.map +1 -0
- package/dist/uuid-C0iMjdcc.js +1 -0
- package/dist/uuid-UlzrVY8Y.mjs +17 -0
- package/dist/vn-0nlIZFLP.mjs +19 -0
- package/dist/vn-B_iut9YL.js +1 -0
- package/dist/walletDetection-JZR3UCOa.mjs +27 -0
- package/dist/walletDetection-bNmV5ItZ.js +1 -0
- package/dist/zh-B4Endr1F.mjs +19 -0
- package/dist/zh-PR82dCHr.js +1 -0
- package/package.json +139 -0
package/README.md
ADDED
|
@@ -0,0 +1,1731 @@
|
|
|
1
|
+
# Cedros Pay
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@cedros/pay-react)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
[](https://bundlephobia.com/package/@cedros/pay-react)
|
|
7
|
+
[]()
|
|
8
|
+
[](https://github.com/cedros-tech/pay-react)
|
|
9
|
+
|
|
10
|
+
> **Unified payments for humans and agents — from Solana Beach to the web.**
|
|
11
|
+
|
|
12
|
+
## ⚠️ BREAKING CHANGES (v2.0.0+)
|
|
13
|
+
|
|
14
|
+
**Version 2.0.0 introduces security improvements that require backend updates.**
|
|
15
|
+
|
|
16
|
+
### What Changed
|
|
17
|
+
|
|
18
|
+
- **NEW:** API v1 versioning (`/paywall/v1/*` endpoints) for stable API contracts
|
|
19
|
+
- **NEW:** Generic endpoints (`/paywall/v1/quote`, `/paywall/v1/verify`) prevent resource ID leakage
|
|
20
|
+
- **REMOVED:** Resource-specific endpoints (`/paywall/{resource}`, `/paywall/cart/{id}`)
|
|
21
|
+
- **SECURITY:** Resource IDs now sent in request bodies and X-PAYMENT headers (not URLs)
|
|
22
|
+
|
|
23
|
+
## 🌅 Why Cedros Pay?
|
|
24
|
+
|
|
25
|
+
Built as a nod to **Solana Beach’s Cedros Avenue**, Cedros Pay embodies the connection between **old-world payments and new-world rails** — Stripe’s fiat system and Solana’s instant, decentralized network.
|
|
26
|
+
|
|
27
|
+
Whether you're building a paywalled blog, an agentic API, or a marketplace, Cedros Pay handles both sides:
|
|
28
|
+
|
|
29
|
+
- Stripe-hosted checkout for credit/debit cards.
|
|
30
|
+
- x402-verified USDC payments for instant crypto settlement.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 🧩 Architecture
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
Frontend (React) → Cedros Server → Stripe & Solana RPC
|
|
38
|
+
| |
|
|
39
|
+
|---- x402 headers ----> |---- Stripe webhooks ----> DB
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Frontend**
|
|
43
|
+
|
|
44
|
+
- React SDK (`@cedros/pay-react`) for drop-in payment buttons.
|
|
45
|
+
- Uses wallet adapters for Solana and Stripe JS SDK for fiat.
|
|
46
|
+
|
|
47
|
+
**Backend**
|
|
48
|
+
|
|
49
|
+
- Go service handling session creation, webhooks, x402 verification, and route protection.
|
|
50
|
+
- Deploy standalone or embed into existing backend.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 💳 Key Features
|
|
55
|
+
|
|
56
|
+
- 🪙 **Dual payment support** — Card + Crypto
|
|
57
|
+
- ⚡ **Instant agentic payments** — Pay per request via x402
|
|
58
|
+
- 🔐 **Stateless & secure** — No need for user accounts or deposit addresses
|
|
59
|
+
- 🌍 **Open source** — MIT-licensed and extensible
|
|
60
|
+
- 🧱 **Minimal integration** — Middleware or proxy for Go APIs
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 🚀 Quick Start
|
|
65
|
+
|
|
66
|
+
### Installation
|
|
67
|
+
|
|
68
|
+
**Option 1: Stripe + Crypto (Full Features)**
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm install @cedros/pay-react \
|
|
72
|
+
@solana/web3.js \
|
|
73
|
+
@solana/spl-token \
|
|
74
|
+
@solana/wallet-adapter-base \
|
|
75
|
+
@solana/wallet-adapter-react \
|
|
76
|
+
@solana/wallet-adapter-react-ui \
|
|
77
|
+
@solana/wallet-adapter-wallets
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Option 2: Stripe Only (Smaller Bundle - ~75KB)**
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm install @cedros/pay-react
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Use the `stripe-only` entry point to get a dramatically smaller bundle:
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import { CedrosProvider, StripeButton } from "@cedros/pay-react/stripe-only";
|
|
90
|
+
import "@cedros/pay-react/style.css";
|
|
91
|
+
|
|
92
|
+
function App() {
|
|
93
|
+
return (
|
|
94
|
+
<CedrosProvider
|
|
95
|
+
config={{
|
|
96
|
+
stripePublicKey: "pk_test_...",
|
|
97
|
+
serverUrl: window.location.origin,
|
|
98
|
+
solanaCluster: "mainnet-beta",
|
|
99
|
+
}}
|
|
100
|
+
>
|
|
101
|
+
<StripeButton resource="item-id" />
|
|
102
|
+
</CedrosProvider>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Bundle Size Comparison:**
|
|
108
|
+
|
|
109
|
+
- `@cedros/pay-react` (full): ~100KB + 850KB Solana peer deps
|
|
110
|
+
- `@cedros/pay-react/stripe-only`: ~75KB (no Solana deps)
|
|
111
|
+
- `@cedros/pay-react/crypto-only`: ~100KB + 850KB Solana peer deps
|
|
112
|
+
|
|
113
|
+
### CDN Usage (Optional)
|
|
114
|
+
|
|
115
|
+
For zero-build prototyping or simple sites, you can import directly from a CDN:
|
|
116
|
+
|
|
117
|
+
```html
|
|
118
|
+
<!-- Styles -->
|
|
119
|
+
<link
|
|
120
|
+
rel="stylesheet"
|
|
121
|
+
href="https://unpkg.com/@cedros/pay-react@0.1.0/dist/style.css"
|
|
122
|
+
/>
|
|
123
|
+
|
|
124
|
+
<!-- Library (ESM) -->
|
|
125
|
+
<script type="module">
|
|
126
|
+
import {
|
|
127
|
+
CedrosProvider,
|
|
128
|
+
StripeButton,
|
|
129
|
+
} from "https://unpkg.com/@cedros/pay-react@0.1.0/dist/index.mjs";
|
|
130
|
+
// Your code here
|
|
131
|
+
</script>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**CDN Options:**
|
|
135
|
+
|
|
136
|
+
- [unpkg.com](https://unpkg.com/@cedros/pay-react) - Fast, reliable, global CDN
|
|
137
|
+
- [jsdelivr.com](https://cdn.jsdelivr.net/npm/@cedros/pay-react) - Multi-CDN with fallback
|
|
138
|
+
|
|
139
|
+
**Performance Notes:**
|
|
140
|
+
|
|
141
|
+
- CDN providers (unpkg, jsdelivr) automatically serve with immutable cache headers (`Cache-Control: public, max-age=31536000, immutable`)
|
|
142
|
+
- For self-hosted deployments, set the same cache headers on `/dist/*` assets for optimal performance
|
|
143
|
+
- Pin to specific version (`@0.1.0`) in production to ensure stability
|
|
144
|
+
|
|
145
|
+
**Option 3: Crypto Only**
|
|
146
|
+
|
|
147
|
+
If you only need Solana crypto payments:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
npm install @cedros/pay-react \
|
|
151
|
+
@solana/web3.js \
|
|
152
|
+
@solana/spl-token \
|
|
153
|
+
@solana/wallet-adapter-base \
|
|
154
|
+
@solana/wallet-adapter-react \
|
|
155
|
+
@solana/wallet-adapter-react-ui \
|
|
156
|
+
@solana/wallet-adapter-wallets
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Then use the `crypto-only` entry point:
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
import { CedrosProvider, CryptoButton } from "@cedros/pay-react/crypto-only";
|
|
163
|
+
import "@cedros/pay-react/style.css";
|
|
164
|
+
|
|
165
|
+
function App() {
|
|
166
|
+
return (
|
|
167
|
+
<CedrosProvider
|
|
168
|
+
config={{
|
|
169
|
+
stripePublicKey: "pk_test_...", // Required even for crypto-only (use a placeholder)
|
|
170
|
+
serverUrl: window.location.origin,
|
|
171
|
+
solanaCluster: "mainnet-beta",
|
|
172
|
+
}}
|
|
173
|
+
>
|
|
174
|
+
<CryptoButton resource="item-id" />
|
|
175
|
+
</CedrosProvider>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Note:** Even when using the `crypto-only` entry point, `stripePublicKey` is still required in the config (use a test/placeholder key if you don't have Stripe integration). This is a known limitation that will be addressed in a future version.
|
|
181
|
+
|
|
182
|
+
**Using the full bundle but hiding crypto button:**
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
<CedrosPay resource="item-id" display={{ showCrypto: false }} />
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Basic Usage
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
import { CedrosPay, CedrosProvider } from "@cedros/pay-react";
|
|
192
|
+
import "@cedros/pay-react/style.css";
|
|
193
|
+
|
|
194
|
+
function App() {
|
|
195
|
+
return (
|
|
196
|
+
<CedrosProvider
|
|
197
|
+
config={{
|
|
198
|
+
stripePublicKey: "pk_test_...",
|
|
199
|
+
serverUrl: window.location.origin,
|
|
200
|
+
solanaCluster: "mainnet-beta",
|
|
201
|
+
// serverUrl defaults to window.location.origin
|
|
202
|
+
// Only needed if your backend is on a different domain
|
|
203
|
+
}}
|
|
204
|
+
>
|
|
205
|
+
<CedrosPay
|
|
206
|
+
resource="demo-item-id-1"
|
|
207
|
+
callbacks={{
|
|
208
|
+
onPaymentSuccess: (result) =>
|
|
209
|
+
console.log("Payment successful!", result.transactionId),
|
|
210
|
+
onPaymentError: (error) =>
|
|
211
|
+
console.error("Payment failed:", error.message),
|
|
212
|
+
}}
|
|
213
|
+
/>
|
|
214
|
+
</CedrosProvider>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Cross-Domain Backend (Optional):**
|
|
220
|
+
If your backend is on a different domain (e.g., `api.example.com` while your frontend is on `example.com`), explicitly set `serverUrl`:
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
<CedrosProvider
|
|
224
|
+
config={{
|
|
225
|
+
stripePublicKey: "pk_test_...",
|
|
226
|
+
serverUrl: "https://api.example.com", // Explicit URL for cross-domain
|
|
227
|
+
solanaCluster: "mainnet-beta",
|
|
228
|
+
}}
|
|
229
|
+
>
|
|
230
|
+
{/* ... */}
|
|
231
|
+
</CedrosProvider>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Backend Setup
|
|
235
|
+
|
|
236
|
+
Your backend must implement the Cedros Pay API endpoints:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
go get github.com/cedros-pay/server
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Required Endpoints (v2.0.0+):**
|
|
243
|
+
|
|
244
|
+
- `GET /cedros-health` - Health check and route discovery
|
|
245
|
+
- `POST /paywall/v1/quote` - x402 payment quote (resource ID in body)
|
|
246
|
+
- `POST /paywall/v1/verify` - Payment verification (resource ID in X-PAYMENT header)
|
|
247
|
+
- `POST /paywall/v1/stripe-session` - Create Stripe checkout (single item)
|
|
248
|
+
- `POST /paywall/v1/cart/checkout` - Create Stripe checkout (cart)
|
|
249
|
+
- `POST /paywall/v1/cart/quote` - Get x402 quote for cart items
|
|
250
|
+
- `POST /paywall/v1/gasless-transaction` - Build gasless transaction (optional)
|
|
251
|
+
- `POST /paywall/v1/nonce` - Generate nonce for admin authentication
|
|
252
|
+
- `POST /paywall/v1/refunds/request` - Create refund request (requires signature from original payer or admin)
|
|
253
|
+
- `POST /paywall/v1/refunds/pending` - Get all pending refunds (admin-only, requires nonce)
|
|
254
|
+
- `POST /paywall/v1/refunds/approve` - Get fresh quote for pending refund (admin-only)
|
|
255
|
+
- `POST /paywall/v1/refunds/deny` - Deny pending refund (admin-only)
|
|
256
|
+
|
|
257
|
+
**Example - Quote Request:**
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
POST /paywall/v1/quote
|
|
261
|
+
Content-Type: application/json
|
|
262
|
+
|
|
263
|
+
{
|
|
264
|
+
"resource": "premium-article",
|
|
265
|
+
"couponCode": "SAVE20" # optional
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
# Response: 402 Payment Required with x402 quote
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**Example - Payment Verification:**
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
POST /paywall/v1/verify
|
|
275
|
+
X-PAYMENT: <base64-encoded-payment-proof>
|
|
276
|
+
|
|
277
|
+
# Payment proof includes resource ID and type
|
|
278
|
+
# No resource IDs in URL path (security improvement)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Example - Refund Request:**
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
POST /paywall/v1/refunds/request
|
|
285
|
+
Content-Type: application/json
|
|
286
|
+
X-Signature: <base64-encoded-signature>
|
|
287
|
+
X-Message: request-refund:<transaction-signature>
|
|
288
|
+
X-Signer: <wallet-address>
|
|
289
|
+
|
|
290
|
+
{
|
|
291
|
+
"originalPurchaseId": "5jHxP...2QvK", // Original transaction signature
|
|
292
|
+
"recipientWallet": "9xQeW...Yhq",
|
|
293
|
+
"amount": 10.5,
|
|
294
|
+
"token": "USDC",
|
|
295
|
+
"reason": "Customer requested refund"
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
# Signer must be the original payer OR admin wallet
|
|
299
|
+
# Recipient wallet must match the payer from original transaction
|
|
300
|
+
# Only one refund allowed per transaction signature
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**Example - Get Pending Refunds (Admin - Nonce Required):**
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
# Step 1: Generate nonce
|
|
307
|
+
POST /paywall/v1/nonce
|
|
308
|
+
Content-Type: application/json
|
|
309
|
+
|
|
310
|
+
{
|
|
311
|
+
"purpose": "list-pending-refunds"
|
|
312
|
+
}
|
|
313
|
+
# Response: { "nonce": "abc123...", "expiresAt": 1234567890 }
|
|
314
|
+
|
|
315
|
+
# Step 2: Fetch pending refunds with nonce
|
|
316
|
+
POST /paywall/v1/refunds/pending
|
|
317
|
+
Content-Type: application/json
|
|
318
|
+
X-Signature: <base64-encoded-signature>
|
|
319
|
+
X-Message: list-pending-refunds:<nonce>
|
|
320
|
+
X-Signer: <admin-wallet-address>
|
|
321
|
+
|
|
322
|
+
# Returns array of pending refund requests
|
|
323
|
+
# Response: [{ refundId, originalPurchaseId, recipientWallet, amount, token, reason, ... }]
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
See [Backend Integration](https://github.com/CedrosPay/server) and `@backend-migration-resource-leakage.md` for complete API reference and migration guide.
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## 🔒 Production Deployment
|
|
331
|
+
|
|
332
|
+
### Content Security Policy (CSP) Headers
|
|
333
|
+
|
|
334
|
+
**⚠️ Important:** Cedros Pay requires specific Content Security Policy directives to function correctly in production. Without these, Stripe and Solana RPC calls will be blocked by the browser.
|
|
335
|
+
|
|
336
|
+
#### Required CSP Directives
|
|
337
|
+
|
|
338
|
+
```http
|
|
339
|
+
Content-Security-Policy:
|
|
340
|
+
script-src 'self' https://js.stripe.com;
|
|
341
|
+
connect-src 'self' https://api.stripe.com https://*.stripe.com https://api.mainnet-beta.solana.com https://*.solana.com;
|
|
342
|
+
frame-src https://js.stripe.com https://checkout.stripe.com;
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**Breakdown:**
|
|
346
|
+
|
|
347
|
+
- `script-src` - Allows Stripe.js to load and execute
|
|
348
|
+
- `connect-src` - Allows API calls to Stripe and Solana RPC endpoints
|
|
349
|
+
- `frame-src` - Allows Stripe Checkout iframe to load
|
|
350
|
+
|
|
351
|
+
#### Framework-Specific Examples
|
|
352
|
+
|
|
353
|
+
**Next.js (App Router)**
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
// next.config.js
|
|
357
|
+
const nextConfig = {
|
|
358
|
+
async headers() {
|
|
359
|
+
return [
|
|
360
|
+
{
|
|
361
|
+
source: "/(.*)",
|
|
362
|
+
headers: [
|
|
363
|
+
{
|
|
364
|
+
key: "Content-Security-Policy",
|
|
365
|
+
value: [
|
|
366
|
+
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.stripe.com",
|
|
367
|
+
"connect-src 'self' https://api.stripe.com https://*.stripe.com https://api.mainnet-beta.solana.com https://*.solana.com https://*.helius-rpc.com https://*.quicknode.pro",
|
|
368
|
+
"frame-src https://js.stripe.com https://checkout.stripe.com",
|
|
369
|
+
].join("; "),
|
|
370
|
+
},
|
|
371
|
+
],
|
|
372
|
+
},
|
|
373
|
+
];
|
|
374
|
+
},
|
|
375
|
+
};
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
**Next.js (Pages Router with Middleware)**
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
// middleware.ts
|
|
382
|
+
import { NextResponse } from "next/server";
|
|
383
|
+
import type { NextRequest } from "next/server";
|
|
384
|
+
|
|
385
|
+
export function middleware(request: NextRequest) {
|
|
386
|
+
const response = NextResponse.next();
|
|
387
|
+
|
|
388
|
+
response.headers.set(
|
|
389
|
+
"Content-Security-Policy",
|
|
390
|
+
[
|
|
391
|
+
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.stripe.com",
|
|
392
|
+
"connect-src 'self' https://api.stripe.com https://*.stripe.com https://api.mainnet-beta.solana.com https://*.solana.com",
|
|
393
|
+
"frame-src https://js.stripe.com https://checkout.stripe.com",
|
|
394
|
+
].join("; ")
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
return response;
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
**Vite (Development)**
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
// vite.config.ts
|
|
405
|
+
import { defineConfig } from "vite";
|
|
406
|
+
|
|
407
|
+
export default defineConfig({
|
|
408
|
+
server: {
|
|
409
|
+
headers: {
|
|
410
|
+
"Content-Security-Policy": [
|
|
411
|
+
"script-src 'self' 'unsafe-inline' https://js.stripe.com",
|
|
412
|
+
"connect-src 'self' https://api.stripe.com https://*.stripe.com https://api.mainnet-beta.solana.com https://*.solana.com",
|
|
413
|
+
"frame-src https://js.stripe.com https://checkout.stripe.com",
|
|
414
|
+
].join("; "),
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
});
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
**Nginx**
|
|
421
|
+
|
|
422
|
+
```nginx
|
|
423
|
+
# nginx.conf
|
|
424
|
+
location / {
|
|
425
|
+
add_header Content-Security-Policy "script-src 'self' https://js.stripe.com; connect-src 'self' https://api.stripe.com https://*.stripe.com https://api.mainnet-beta.solana.com https://*.solana.com; frame-src https://js.stripe.com https://checkout.stripe.com;" always;
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
**Express.js**
|
|
430
|
+
|
|
431
|
+
```javascript
|
|
432
|
+
// server.js
|
|
433
|
+
const helmet = require("helmet");
|
|
434
|
+
|
|
435
|
+
app.use(
|
|
436
|
+
helmet.contentSecurityPolicy({
|
|
437
|
+
directives: {
|
|
438
|
+
scriptSrc: ["'self'", "https://js.stripe.com"],
|
|
439
|
+
connectSrc: [
|
|
440
|
+
"'self'",
|
|
441
|
+
"https://api.stripe.com",
|
|
442
|
+
"https://*.stripe.com",
|
|
443
|
+
"https://api.mainnet-beta.solana.com",
|
|
444
|
+
"https://*.solana.com",
|
|
445
|
+
],
|
|
446
|
+
frameSrc: ["https://js.stripe.com", "https://checkout.stripe.com"],
|
|
447
|
+
},
|
|
448
|
+
})
|
|
449
|
+
);
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
**HTML Meta Tag (Not Recommended)**
|
|
453
|
+
|
|
454
|
+
```html
|
|
455
|
+
<!-- Use server headers instead when possible -->
|
|
456
|
+
<meta
|
|
457
|
+
http-equiv="Content-Security-Policy"
|
|
458
|
+
content="script-src 'self' https://js.stripe.com; connect-src 'self' https://api.stripe.com https://*.stripe.com https://api.mainnet-beta.solana.com https://*.solana.com; frame-src https://js.stripe.com https://checkout.stripe.com;"
|
|
459
|
+
/>
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
#### Custom RPC Endpoints
|
|
463
|
+
|
|
464
|
+
If you're using a custom Solana RPC provider (Helius, QuickNode, etc.), add their domains to `connect-src`:
|
|
465
|
+
|
|
466
|
+
```http
|
|
467
|
+
connect-src 'self'
|
|
468
|
+
https://api.stripe.com
|
|
469
|
+
https://*.stripe.com
|
|
470
|
+
https://mainnet.helius-rpc.com
|
|
471
|
+
https://*.quicknode.pro
|
|
472
|
+
https://rpc.ankr.com;
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
#### Devnet/Testnet
|
|
476
|
+
|
|
477
|
+
For development against Solana devnet or testnet:
|
|
478
|
+
|
|
479
|
+
```http
|
|
480
|
+
connect-src 'self'
|
|
481
|
+
https://api.stripe.com
|
|
482
|
+
https://*.stripe.com
|
|
483
|
+
https://api.devnet.solana.com
|
|
484
|
+
https://api.testnet.solana.com;
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### Troubleshooting CSP Issues
|
|
488
|
+
|
|
489
|
+
**Symptom:** Stripe Checkout doesn't load or throws CORS errors
|
|
490
|
+
|
|
491
|
+
```
|
|
492
|
+
Refused to load the script 'https://js.stripe.com/v3/' because it violates the following Content Security Policy directive: "script-src 'self'"
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
**Fix:** Add `https://js.stripe.com` to `script-src`
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
**Symptom:** Solana RPC calls fail with network errors
|
|
500
|
+
|
|
501
|
+
```
|
|
502
|
+
Refused to connect to 'https://api.mainnet-beta.solana.com' because it violates the following Content Security Policy directive: "connect-src 'self'"
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
**Fix:** Add your Solana RPC endpoint to `connect-src`
|
|
506
|
+
|
|
507
|
+
---
|
|
508
|
+
|
|
509
|
+
**Symptom:** Stripe Checkout redirects fail or show blank page
|
|
510
|
+
|
|
511
|
+
```
|
|
512
|
+
Refused to display 'https://checkout.stripe.com' in a frame because it violates the following Content Security Policy directive: "frame-src 'self'"
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
**Fix:** Add `https://checkout.stripe.com` to `frame-src`
|
|
516
|
+
|
|
517
|
+
---
|
|
518
|
+
|
|
519
|
+
### Testing CSP in Development
|
|
520
|
+
|
|
521
|
+
1. Open browser DevTools → Console
|
|
522
|
+
2. Look for CSP violation warnings (usually in red)
|
|
523
|
+
3. Check the Network tab for blocked requests
|
|
524
|
+
4. Add blocked domains to appropriate CSP directives
|
|
525
|
+
|
|
526
|
+
**Chrome DevTools Example:**
|
|
527
|
+
|
|
528
|
+
```
|
|
529
|
+
[Report Only] Refused to connect to 'https://api.stripe.com/v1/tokens'
|
|
530
|
+
because it violates the document's Content Security Policy.
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
### Best Practices
|
|
534
|
+
|
|
535
|
+
✅ **DO:**
|
|
536
|
+
|
|
537
|
+
- Use server-side headers (not meta tags) for CSP
|
|
538
|
+
- Test CSP in staging before deploying to production
|
|
539
|
+
- Use wildcards sparingly (`*.stripe.com` is okay, `*` is not)
|
|
540
|
+
- Include your custom RPC provider domains
|
|
541
|
+
|
|
542
|
+
❌ **DON'T:**
|
|
543
|
+
|
|
544
|
+
- Use `'unsafe-inline'` in production unless necessary
|
|
545
|
+
- Block Stripe or Solana domains
|
|
546
|
+
- Forget to add `frame-src` for Stripe Checkout
|
|
547
|
+
- Use overly permissive directives like `* 'unsafe-eval'`
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
### CSP Helper Generator
|
|
552
|
+
|
|
553
|
+
**⚠️ RECOMMENDED:** Use the `generateCSP()` helper to automatically generate correct CSP directives for your configuration. This prevents common misconfigurations that break payment widgets.
|
|
554
|
+
|
|
555
|
+
#### Quick Start
|
|
556
|
+
|
|
557
|
+
```typescript
|
|
558
|
+
import { generateCSP, RPC_PROVIDERS } from "@cedros/pay-react";
|
|
559
|
+
|
|
560
|
+
// Generate CSP for production with custom RPC
|
|
561
|
+
const csp = generateCSP({
|
|
562
|
+
solanaCluster: "mainnet-beta",
|
|
563
|
+
solanaEndpoint: "https://mainnet.helius-rpc.com",
|
|
564
|
+
allowUnsafeScripts: true, // Required for Next.js
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
// Use in your framework
|
|
568
|
+
response.setHeader("Content-Security-Policy", csp);
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
#### Configuration Options
|
|
572
|
+
|
|
573
|
+
```typescript
|
|
574
|
+
interface CSPConfig {
|
|
575
|
+
solanaCluster?: "mainnet-beta" | "devnet" | "testnet";
|
|
576
|
+
solanaEndpoint?: string; // Custom RPC URL
|
|
577
|
+
customRpcProviders?: string[]; // Additional RPC providers
|
|
578
|
+
allowUnsafeScripts?: boolean; // For Next.js, etc.
|
|
579
|
+
additionalScriptSrc?: string[];
|
|
580
|
+
additionalConnectSrc?: string[];
|
|
581
|
+
additionalFrameSrc?: string[];
|
|
582
|
+
includeStripe?: boolean; // Set false for crypto-only
|
|
583
|
+
}
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
#### Framework Examples
|
|
587
|
+
|
|
588
|
+
**Next.js App Router:**
|
|
589
|
+
|
|
590
|
+
```typescript
|
|
591
|
+
// next.config.js
|
|
592
|
+
import { generateCSP } from "@cedros/pay-react";
|
|
593
|
+
|
|
594
|
+
const csp = generateCSP({
|
|
595
|
+
solanaCluster: "mainnet-beta",
|
|
596
|
+
solanaEndpoint: process.env.SOLANA_RPC_URL,
|
|
597
|
+
allowUnsafeScripts: true,
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
const nextConfig = {
|
|
601
|
+
async headers() {
|
|
602
|
+
return [
|
|
603
|
+
{
|
|
604
|
+
source: "/(.*)",
|
|
605
|
+
headers: [{ key: "Content-Security-Policy", value: csp }],
|
|
606
|
+
},
|
|
607
|
+
];
|
|
608
|
+
},
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
export default nextConfig;
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
**Express with Helmet:**
|
|
615
|
+
|
|
616
|
+
```typescript
|
|
617
|
+
import { generateCSP } from "@cedros/pay-react";
|
|
618
|
+
import helmet from "helmet";
|
|
619
|
+
|
|
620
|
+
const cspDirectives = generateCSP(
|
|
621
|
+
{
|
|
622
|
+
solanaCluster: "mainnet-beta",
|
|
623
|
+
solanaEndpoint: process.env.SOLANA_RPC_URL,
|
|
624
|
+
},
|
|
625
|
+
"helmet" // Returns object format for helmet
|
|
626
|
+
);
|
|
627
|
+
|
|
628
|
+
app.use(helmet.contentSecurityPolicy({ directives: cspDirectives }));
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
**Vite Development:**
|
|
632
|
+
|
|
633
|
+
```typescript
|
|
634
|
+
// vite.config.ts
|
|
635
|
+
import { defineConfig } from "vite";
|
|
636
|
+
import { generateCSP } from "@cedros/pay-react";
|
|
637
|
+
|
|
638
|
+
const csp = generateCSP({
|
|
639
|
+
solanaCluster: "devnet",
|
|
640
|
+
allowUnsafeScripts: true,
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
export default defineConfig({
|
|
644
|
+
server: {
|
|
645
|
+
headers: {
|
|
646
|
+
"Content-Security-Policy": csp,
|
|
647
|
+
},
|
|
648
|
+
},
|
|
649
|
+
});
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
#### Presets
|
|
653
|
+
|
|
654
|
+
Use presets for common scenarios:
|
|
655
|
+
|
|
656
|
+
```typescript
|
|
657
|
+
import { generateCSP, CSP_PRESETS } from "@cedros/pay-react";
|
|
658
|
+
|
|
659
|
+
// Production mainnet with custom RPC
|
|
660
|
+
const csp1 = generateCSP(
|
|
661
|
+
CSP_PRESETS.MAINNET_CUSTOM_RPC("https://mainnet.helius-rpc.com")
|
|
662
|
+
);
|
|
663
|
+
|
|
664
|
+
// Next.js with mainnet
|
|
665
|
+
const csp2 = generateCSP(
|
|
666
|
+
CSP_PRESETS.MAINNET_NEXTJS("https://mainnet.helius-rpc.com")
|
|
667
|
+
);
|
|
668
|
+
|
|
669
|
+
// Devnet testing
|
|
670
|
+
const csp3 = generateCSP(CSP_PRESETS.DEVNET());
|
|
671
|
+
|
|
672
|
+
// Crypto-only (no Stripe)
|
|
673
|
+
const csp4 = generateCSP(CSP_PRESETS.CRYPTO_ONLY());
|
|
674
|
+
|
|
675
|
+
// Stripe-only (no Solana)
|
|
676
|
+
const csp5 = generateCSP(CSP_PRESETS.STRIPE_ONLY());
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
#### Common RPC Providers
|
|
680
|
+
|
|
681
|
+
```typescript
|
|
682
|
+
import { RPC_PROVIDERS } from "@cedros/pay-react";
|
|
683
|
+
|
|
684
|
+
const csp = generateCSP({
|
|
685
|
+
customRpcProviders: [
|
|
686
|
+
RPC_PROVIDERS.HELIUS, // https://*.helius-rpc.com
|
|
687
|
+
RPC_PROVIDERS.QUICKNODE, // https://*.quicknode.pro
|
|
688
|
+
RPC_PROVIDERS.ALCHEMY, // https://*.alchemy.com
|
|
689
|
+
RPC_PROVIDERS.ANKR, // https://rpc.ankr.com
|
|
690
|
+
RPC_PROVIDERS.TRITON, // https://*.rpcpool.com
|
|
691
|
+
],
|
|
692
|
+
});
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
#### Output Formats
|
|
696
|
+
|
|
697
|
+
The helper supports multiple output formats:
|
|
698
|
+
|
|
699
|
+
```typescript
|
|
700
|
+
// HTTP header format (default)
|
|
701
|
+
const header = generateCSP(config, "header");
|
|
702
|
+
// "script-src 'self' https://js.stripe.com; connect-src ..."
|
|
703
|
+
|
|
704
|
+
// HTML meta tag format
|
|
705
|
+
const meta = generateCSP(config, "meta");
|
|
706
|
+
|
|
707
|
+
// Next.js config format
|
|
708
|
+
const nextjs = generateCSP(config, "nextjs");
|
|
709
|
+
|
|
710
|
+
// Express helmet format (object)
|
|
711
|
+
const helmet = generateCSP(config, "helmet");
|
|
712
|
+
// { scriptSrc: [...], connectSrc: [...], frameSrc: [...] }
|
|
713
|
+
|
|
714
|
+
// Nginx config format
|
|
715
|
+
const nginx = generateCSP(config, "nginx");
|
|
716
|
+
|
|
717
|
+
// Raw directives object
|
|
718
|
+
const directives = generateCSP(config, "directives");
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
#### Why Use the Helper?
|
|
722
|
+
|
|
723
|
+
✅ **Prevents common errors:**
|
|
724
|
+
|
|
725
|
+
- Forgetting Solana RPC endpoints
|
|
726
|
+
- Missing Stripe iframe domains
|
|
727
|
+
- Wrong cluster URLs (devnet vs mainnet)
|
|
728
|
+
|
|
729
|
+
✅ **Type-safe configuration:**
|
|
730
|
+
|
|
731
|
+
- TypeScript autocomplete for all options
|
|
732
|
+
- Validates cluster names
|
|
733
|
+
- Catches typos at compile time
|
|
734
|
+
|
|
735
|
+
✅ **Framework-agnostic:**
|
|
736
|
+
|
|
737
|
+
- Works with Next.js, Express, Vite, Nginx, etc.
|
|
738
|
+
- Multiple output formats
|
|
739
|
+
- No dependencies
|
|
740
|
+
|
|
741
|
+
---
|
|
742
|
+
|
|
743
|
+
### Security Best Practices
|
|
744
|
+
|
|
745
|
+
#### Subresource Integrity (SRI) for Stripe.js
|
|
746
|
+
|
|
747
|
+
**⚠️ IMPORTANT: Cedros Pay does NOT use SRI hashes for Stripe.js, and this is intentional.**
|
|
748
|
+
|
|
749
|
+
**Why SRI is NOT used:**
|
|
750
|
+
|
|
751
|
+
- **Stripe updates frequently** - Security patches and bug fixes are pushed without URL changes
|
|
752
|
+
- **SRI breaks automatic updates** - Hardcoded hashes prevent receiving critical security fixes
|
|
753
|
+
- **Stripe's official recommendation** - Stripe explicitly advises against using SRI
|
|
754
|
+
- **Alternative protection** - Content Security Policy (CSP) provides the security layer
|
|
755
|
+
|
|
756
|
+
**From Stripe's documentation:**
|
|
757
|
+
|
|
758
|
+
> "We do not recommend using Subresource Integrity (SRI) with Stripe.js. Stripe.js is served from a highly-available CDN, and we regularly update the library to address security issues and improve functionality. Using SRI would prevent you from receiving these automatic updates."
|
|
759
|
+
|
|
760
|
+
**How Cedros Pay Protects Against CDN Compromise:**
|
|
761
|
+
|
|
762
|
+
1. **Content Security Policy (CSP)**
|
|
763
|
+
|
|
764
|
+
```http
|
|
765
|
+
Content-Security-Policy: script-src 'self' https://js.stripe.com
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
- Prevents loading scripts from unauthorized domains
|
|
769
|
+
- Blocks inline scripts and eval()
|
|
770
|
+
- Works with Stripe's automatic updates
|
|
771
|
+
|
|
772
|
+
2. **Package Integrity via npm**
|
|
773
|
+
|
|
774
|
+
```json
|
|
775
|
+
{
|
|
776
|
+
"dependencies": {
|
|
777
|
+
"@stripe/stripe-js": "^2.4.0"
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
- `package-lock.json` contains integrity hashes for npm packages
|
|
783
|
+
- npm verifies package integrity on installation
|
|
784
|
+
- Protects against tampering with the loader
|
|
785
|
+
|
|
786
|
+
3. **HTTPS Enforcement**
|
|
787
|
+
|
|
788
|
+
- Stripe.js is loaded over HTTPS only
|
|
789
|
+
- Modern browsers enforce secure connections
|
|
790
|
+
- Certificate pinning via browser trust store
|
|
791
|
+
|
|
792
|
+
4. **Version Pinning** (optional)
|
|
793
|
+
```json
|
|
794
|
+
{
|
|
795
|
+
"dependencies": {
|
|
796
|
+
"@stripe/stripe-js": "2.4.0" // Exact version (no caret)
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
```
|
|
800
|
+
- Prevents unexpected updates
|
|
801
|
+
- Review changelog before upgrading
|
|
802
|
+
- Balance security updates vs. stability
|
|
803
|
+
|
|
804
|
+
**Recommended Security Checklist:**
|
|
805
|
+
|
|
806
|
+
✅ **DO:**
|
|
807
|
+
|
|
808
|
+
- Use CSP headers with `script-src https://js.stripe.com`
|
|
809
|
+
- Keep `@stripe/stripe-js` updated for security patches
|
|
810
|
+
- Use HTTPS for all connections
|
|
811
|
+
- Enable npm package auditing (`npm audit`)
|
|
812
|
+
- Review Stripe's changelog before major updates
|
|
813
|
+
- Monitor Stripe's security advisories
|
|
814
|
+
|
|
815
|
+
❌ **DON'T:**
|
|
816
|
+
|
|
817
|
+
- Add SRI hashes to Stripe.js (breaks updates)
|
|
818
|
+
- Allow `script-src *` in CSP (too permissive)
|
|
819
|
+
- Use outdated versions of @stripe/stripe-js
|
|
820
|
+
- Load Stripe.js from third-party CDNs
|
|
821
|
+
- Disable HTTPS enforcement
|
|
822
|
+
|
|
823
|
+
**Alternative: Self-Hosting Stripe.js (NOT RECOMMENDED)**
|
|
824
|
+
|
|
825
|
+
While technically possible to self-host Stripe.js with SRI, Stripe strongly discourages this:
|
|
826
|
+
|
|
827
|
+
- ❌ Miss critical security updates
|
|
828
|
+
- ❌ Break PCI DSS compliance requirements
|
|
829
|
+
- ❌ Lose Stripe's CDN performance benefits
|
|
830
|
+
- ❌ Violate Stripe's Terms of Service
|
|
831
|
+
|
|
832
|
+
**For maximum security, follow Stripe's recommendations and use CSP instead of SRI.**
|
|
833
|
+
|
|
834
|
+
---
|
|
835
|
+
|
|
836
|
+
## 🌍 Internationalization (i18n)
|
|
837
|
+
|
|
838
|
+
Cedros Pay supports multiple languages with automatic browser locale detection and zero-configuration setup.
|
|
839
|
+
|
|
840
|
+
### Supported Languages
|
|
841
|
+
|
|
842
|
+
Currently available (auto-detected from `src/i18n/translations/` folder):
|
|
843
|
+
|
|
844
|
+
- 🇺🇸 **English** (en) - Default
|
|
845
|
+
- 🇪🇸 **Spanish** (es)
|
|
846
|
+
|
|
847
|
+
### Usage
|
|
848
|
+
|
|
849
|
+
**Automatic (recommended):**
|
|
850
|
+
|
|
851
|
+
```tsx
|
|
852
|
+
import { useTranslation } from "@cedros/pay-react";
|
|
853
|
+
|
|
854
|
+
function PaymentButton() {
|
|
855
|
+
const { t } = useTranslation(); // Auto-detects browser language
|
|
856
|
+
|
|
857
|
+
return (
|
|
858
|
+
<button>{t("ui.pay_with_card")}</button> // "Pay with Card" or "Pagar con Tarjeta"
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
**Manual locale override:**
|
|
864
|
+
|
|
865
|
+
```tsx
|
|
866
|
+
function SpanishOnlyButton() {
|
|
867
|
+
const { t } = useTranslation("es"); // Force Spanish
|
|
868
|
+
return <button>{t("ui.pay_with_card")}</button>; // Always "Pagar con Tarjeta"
|
|
869
|
+
}
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
**Error messages (automatic):**
|
|
873
|
+
|
|
874
|
+
```tsx
|
|
875
|
+
import { PaymentError } from "@cedros/pay-react";
|
|
876
|
+
|
|
877
|
+
// Errors are automatically localized based on user's browser language
|
|
878
|
+
error.getUserMessage(); // Returns localized message + action
|
|
879
|
+
error.getShortMessage(); // Returns just the message (no action)
|
|
880
|
+
error.getAction(); // Returns just the action guidance
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
### Available Translation Keys
|
|
884
|
+
|
|
885
|
+
**UI Labels:**
|
|
886
|
+
|
|
887
|
+
- `ui.pay_with_card` - "Pay with Card"
|
|
888
|
+
- `ui.pay_with_crypto` - "Pay with USDC"
|
|
889
|
+
- `ui.connect_wallet` - "Connect Wallet"
|
|
890
|
+
- `ui.processing` - "Processing..."
|
|
891
|
+
- `ui.loading` - "Loading..."
|
|
892
|
+
- `ui.close` - "Close"
|
|
893
|
+
- `ui.cancel` - "Cancel"
|
|
894
|
+
- `ui.confirm` - "Confirm"
|
|
895
|
+
- `ui.retry` - "Try Again"
|
|
896
|
+
- `ui.contact_support` - "Contact Support"
|
|
897
|
+
|
|
898
|
+
**Error Messages:**
|
|
899
|
+
|
|
900
|
+
```tsx
|
|
901
|
+
t("errors.insufficient_funds_token.message"); // "Insufficient balance in your wallet"
|
|
902
|
+
t("errors.insufficient_funds_token.action"); // "Add more funds to your wallet and try again."
|
|
903
|
+
```
|
|
904
|
+
|
|
905
|
+
**Wallet Messages:**
|
|
906
|
+
|
|
907
|
+
```tsx
|
|
908
|
+
t("wallet.no_wallet_detected"); // "No Solana wallet detected"
|
|
909
|
+
t("wallet.install_wallet"); // "Please install a Solana wallet..."
|
|
910
|
+
t("wallet.wallet_connection_failed"); // "Failed to connect wallet"
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
### Adding New Languages
|
|
914
|
+
|
|
915
|
+
1. **Create translation file** in `src/i18n/translations/{locale}.json`:
|
|
916
|
+
|
|
917
|
+
```json
|
|
918
|
+
// src/i18n/translations/fr.json
|
|
919
|
+
{
|
|
920
|
+
"locale": "fr",
|
|
921
|
+
"ui": {
|
|
922
|
+
"pay_with_card": "Payer par Carte",
|
|
923
|
+
"pay_with_crypto": "Payer avec USDC",
|
|
924
|
+
...
|
|
925
|
+
},
|
|
926
|
+
"errors": { ... }
|
|
927
|
+
}
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
2. **That's it!** The system automatically detects new files and loads them.
|
|
931
|
+
|
|
932
|
+
See `src/i18n/TRANSLATION_INSTRUCTIONS.md` for detailed translation guidelines.
|
|
933
|
+
|
|
934
|
+
### Dynamic Language Loading
|
|
935
|
+
|
|
936
|
+
The i18n system:
|
|
937
|
+
|
|
938
|
+
- ✅ Auto-detects available languages from file system
|
|
939
|
+
- ✅ Only loads the language the user needs (tree-shakeable)
|
|
940
|
+
- ✅ Falls back to English if translation missing
|
|
941
|
+
- ✅ Zero configuration required
|
|
942
|
+
|
|
943
|
+
---
|
|
944
|
+
|
|
945
|
+
## 🔄 Type Versioning Policy
|
|
946
|
+
|
|
947
|
+
Cedros Pay uses **semantic versioning for TypeScript types** to prevent breaking changes from affecting your code.
|
|
948
|
+
|
|
949
|
+
### How It Works
|
|
950
|
+
|
|
951
|
+
All types are exported in versioned namespaces (`v1`, `v2`, etc.):
|
|
952
|
+
|
|
953
|
+
```tsx
|
|
954
|
+
// Recommended: Use top-level exports (always points to current stable version)
|
|
955
|
+
import { X402Requirement, PaymentResult } from "@cedros/pay-react";
|
|
956
|
+
|
|
957
|
+
// Explicit version (locks to v1, won't break on v2 release)
|
|
958
|
+
import { v1 } from "@cedros/pay-react";
|
|
959
|
+
const requirement: v1.X402Requirement = {
|
|
960
|
+
/* ... */
|
|
961
|
+
};
|
|
962
|
+
|
|
963
|
+
// Future: When v2 is released, you can migrate gradually
|
|
964
|
+
import { v2 } from "@cedros/pay-react";
|
|
965
|
+
const newRequirement: v2.X402Requirement = {
|
|
966
|
+
/* ... */
|
|
967
|
+
};
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
### Breaking Change Example
|
|
971
|
+
|
|
972
|
+
If we need to change `X402Requirement.maxAmountRequired` from `string` to `bigint`:
|
|
973
|
+
|
|
974
|
+
1. **v1 namespace remains unchanged** - Your existing code keeps working
|
|
975
|
+
2. **v2 namespace** is created with the new type
|
|
976
|
+
3. **Top-level exports** point to v2 (with major version bump)
|
|
977
|
+
4. You can migrate at your own pace:
|
|
978
|
+
|
|
979
|
+
```tsx
|
|
980
|
+
// Your old code still works with v1
|
|
981
|
+
import { v1 } from "@cedros/pay-react";
|
|
982
|
+
const oldReq: v1.X402Requirement = { maxAmountRequired: "1000000" };
|
|
983
|
+
|
|
984
|
+
// New code uses v2
|
|
985
|
+
import { v2 } from "@cedros/pay-react";
|
|
986
|
+
const newReq: v2.X402Requirement = { maxAmountRequired: 1000000n };
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
### Stability Guarantee
|
|
990
|
+
|
|
991
|
+
- **v1 types are frozen** - No breaking changes, ever
|
|
992
|
+
- **Top-level exports** may change across major versions
|
|
993
|
+
- **Older versions remain available** for backward compatibility
|
|
994
|
+
- **Clear migration path** when breaking changes are necessary
|
|
995
|
+
|
|
996
|
+
---
|
|
997
|
+
|
|
998
|
+
## 📖 Core Concepts
|
|
999
|
+
|
|
1000
|
+
### Single Item Purchase
|
|
1001
|
+
|
|
1002
|
+
```tsx
|
|
1003
|
+
<CedrosPay
|
|
1004
|
+
resource="article-123"
|
|
1005
|
+
callbacks={{
|
|
1006
|
+
onPaymentSuccess: (result) => unlockContent(result.transactionId),
|
|
1007
|
+
}}
|
|
1008
|
+
/>
|
|
1009
|
+
```
|
|
1010
|
+
|
|
1011
|
+
### Cart Checkout (Multiple Items)
|
|
1012
|
+
|
|
1013
|
+
```tsx
|
|
1014
|
+
<CedrosPay
|
|
1015
|
+
items={[
|
|
1016
|
+
{ resource: "product-1", quantity: 2 },
|
|
1017
|
+
{ resource: "product-2", quantity: 1 },
|
|
1018
|
+
]}
|
|
1019
|
+
callbacks={{
|
|
1020
|
+
onPaymentSuccess: (result) => processOrder(result.transactionId),
|
|
1021
|
+
}}
|
|
1022
|
+
/>
|
|
1023
|
+
```
|
|
1024
|
+
|
|
1025
|
+
### Coupon Codes
|
|
1026
|
+
|
|
1027
|
+
```tsx
|
|
1028
|
+
<CedrosPay
|
|
1029
|
+
resource="premium-content"
|
|
1030
|
+
checkout={{
|
|
1031
|
+
couponCode: "LAUNCH50", // Pass from user input or auto-apply
|
|
1032
|
+
}}
|
|
1033
|
+
/>
|
|
1034
|
+
```
|
|
1035
|
+
|
|
1036
|
+
**Coupon Stacking Supported!** Unlimited auto-apply coupons can stack with 1 manual coupon code. Percentage discounts apply first (multiplicatively), then fixed discounts are subtracted.
|
|
1037
|
+
|
|
1038
|
+
#### Two-Phase Coupon System
|
|
1039
|
+
|
|
1040
|
+
Coupons are applied in two phases to provide clear pricing transparency:
|
|
1041
|
+
|
|
1042
|
+
1. **Catalog-level coupons** - Product-specific discounts shown on product pages
|
|
1043
|
+
|
|
1044
|
+
- Configured with `applies_at: catalog` and specific `product_ids`
|
|
1045
|
+
- Example: "20% off this specific item"
|
|
1046
|
+
- Discounted price shown immediately when viewing the product
|
|
1047
|
+
|
|
1048
|
+
2. **Checkout-level coupons** - Site-wide promotions applied at cart
|
|
1049
|
+
- Configured with `applies_at: checkout` and `scope: all`
|
|
1050
|
+
- Example: "10% off your entire order"
|
|
1051
|
+
- Applied after catalog discounts at checkout
|
|
1052
|
+
|
|
1053
|
+
**Single Product Quote Response:**
|
|
1054
|
+
|
|
1055
|
+
```json
|
|
1056
|
+
{
|
|
1057
|
+
"crypto": {
|
|
1058
|
+
"maxAmountRequired": "184000", // Actual amount to charge (atomic units)
|
|
1059
|
+
"extra": {
|
|
1060
|
+
"original_amount": "1.000000",
|
|
1061
|
+
"discounted_amount": "0.184000",
|
|
1062
|
+
"applied_coupons": "PRODUCT20,SITE10,CRYPTO5AUTO,FIXED5", // All applied
|
|
1063
|
+
"catalog_coupons": "PRODUCT20", // Product-specific
|
|
1064
|
+
"checkout_coupons": "SITE10,CRYPTO5AUTO,FIXED5", // Site-wide
|
|
1065
|
+
"decimals": 6
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1071
|
+
**Cart Quote Response:**
|
|
1072
|
+
|
|
1073
|
+
```json
|
|
1074
|
+
{
|
|
1075
|
+
"totalAmount": 2.7661,
|
|
1076
|
+
"metadata": {
|
|
1077
|
+
"subtotal_after_catalog": "3.820000",
|
|
1078
|
+
"discounted_amount": "2.766100",
|
|
1079
|
+
"catalog_coupons": "PRODUCT20",
|
|
1080
|
+
"checkout_coupons": "SITE10,CRYPTO5AUTO,FIXED5",
|
|
1081
|
+
"coupon_codes": "PRODUCT20,SITE10,CRYPTO5AUTO,FIXED5"
|
|
1082
|
+
},
|
|
1083
|
+
"items": [
|
|
1084
|
+
{
|
|
1085
|
+
"resource": "item-1",
|
|
1086
|
+
"priceAmount": 0.8, // After catalog discount
|
|
1087
|
+
"originalPrice": 1.0,
|
|
1088
|
+
"appliedCoupons": ["PRODUCT20"]
|
|
1089
|
+
}
|
|
1090
|
+
]
|
|
1091
|
+
}
|
|
1092
|
+
```
|
|
1093
|
+
|
|
1094
|
+
**Display Guidelines:**
|
|
1095
|
+
|
|
1096
|
+
- **Product pages:** Show strikethrough original price with catalog discount
|
|
1097
|
+
- **Cart:** Show catalog discounts on items, checkout discounts in summary
|
|
1098
|
+
- **Always use `maxAmountRequired` for actual transactions** - `extra` fields are display-only
|
|
1099
|
+
|
|
1100
|
+
Coupons are configured server-side with:
|
|
1101
|
+
|
|
1102
|
+
- Percentage or fixed amount discounts
|
|
1103
|
+
- Expiration dates
|
|
1104
|
+
- Usage limits
|
|
1105
|
+
- Auto-apply functionality
|
|
1106
|
+
- Payment method filtering (Stripe-only, x402-only, or both)
|
|
1107
|
+
- Phase configuration (`applies_at: catalog` or `checkout`)
|
|
1108
|
+
|
|
1109
|
+
After a successful x402 payment, parse applied coupons from the settlement response:
|
|
1110
|
+
|
|
1111
|
+
```tsx
|
|
1112
|
+
import {
|
|
1113
|
+
parseCouponCodes,
|
|
1114
|
+
calculateDiscountPercentage,
|
|
1115
|
+
} from "@cedros/pay-react";
|
|
1116
|
+
|
|
1117
|
+
// Parse applied coupons
|
|
1118
|
+
const appliedCoupons = parseCouponCodes(settlement.metadata);
|
|
1119
|
+
// ["SITE10", "CRYPTO5AUTO", "SAVE20"]
|
|
1120
|
+
|
|
1121
|
+
// Calculate total discount percentage
|
|
1122
|
+
const discountPercent = calculateDiscountPercentage(
|
|
1123
|
+
parseFloat(settlement.metadata.original_amount),
|
|
1124
|
+
parseFloat(settlement.metadata.discounted_amount)
|
|
1125
|
+
);
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
### Theme Customization
|
|
1129
|
+
|
|
1130
|
+
```tsx
|
|
1131
|
+
<CedrosProvider
|
|
1132
|
+
config={{
|
|
1133
|
+
stripePublicKey: "pk_test_...",
|
|
1134
|
+
serverUrl: window.location.origin,
|
|
1135
|
+
solanaCluster: "mainnet-beta",
|
|
1136
|
+
theme: "dark", // "light" or "dark"
|
|
1137
|
+
themeOverrides: {
|
|
1138
|
+
stripeBackground: "#6366f1",
|
|
1139
|
+
cryptoBackground: "#0ea5e9",
|
|
1140
|
+
buttonBorderRadius: "12px",
|
|
1141
|
+
},
|
|
1142
|
+
}}
|
|
1143
|
+
>
|
|
1144
|
+
{/* Your app */}
|
|
1145
|
+
</CedrosProvider>
|
|
1146
|
+
```
|
|
1147
|
+
|
|
1148
|
+
### Unstyled Mode (Custom Design Systems)
|
|
1149
|
+
|
|
1150
|
+
For complete control over styling, use the `unstyled` prop to disable all default styles:
|
|
1151
|
+
|
|
1152
|
+
```tsx
|
|
1153
|
+
<CedrosProvider
|
|
1154
|
+
config={{
|
|
1155
|
+
stripePublicKey: "pk_test_...",
|
|
1156
|
+
serverUrl: window.location.origin,
|
|
1157
|
+
solanaCluster: "mainnet-beta",
|
|
1158
|
+
unstyled: true, // Disables all default CSS classes and styles
|
|
1159
|
+
}}
|
|
1160
|
+
>
|
|
1161
|
+
<CedrosPay
|
|
1162
|
+
resource="item-id"
|
|
1163
|
+
display={{ className: "my-custom-button-class" }}
|
|
1164
|
+
/>
|
|
1165
|
+
</CedrosProvider>
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
**Why use unstyled mode?**
|
|
1169
|
+
|
|
1170
|
+
- Build custom design systems without fighting CSS specificity
|
|
1171
|
+
- Use your own CSS framework (Tailwind, Material UI, etc.)
|
|
1172
|
+
- Full control over component appearance and behavior
|
|
1173
|
+
- No need to override or reset default styles
|
|
1174
|
+
|
|
1175
|
+
**What gets disabled:**
|
|
1176
|
+
|
|
1177
|
+
- All `cedros-theme__*` CSS classes
|
|
1178
|
+
- Default inline styles from theme tokens
|
|
1179
|
+
- Button styling (stripe/crypto gradients, hover effects)
|
|
1180
|
+
- Error/success message styling
|
|
1181
|
+
|
|
1182
|
+
**What you still get:**
|
|
1183
|
+
|
|
1184
|
+
- All payment logic and wallet integration
|
|
1185
|
+
- Event handlers and callbacks
|
|
1186
|
+
- Component structure and behavior
|
|
1187
|
+
- Props like `className` for your custom styling
|
|
1188
|
+
|
|
1189
|
+
---
|
|
1190
|
+
|
|
1191
|
+
## 🎛️ Props Reference
|
|
1192
|
+
|
|
1193
|
+
### CedrosProvider Configuration
|
|
1194
|
+
|
|
1195
|
+
| Prop | Type | Description |
|
|
1196
|
+
| ----------------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1197
|
+
| `stripePublicKey` | `string` | Stripe publishable key (required) |
|
|
1198
|
+
| `solanaCluster` | `'mainnet-beta' \| 'devnet' \| 'testnet'` | Solana network (required) |
|
|
1199
|
+
| `serverUrl` | `string` | Backend API URL (defaults to current origin) |
|
|
1200
|
+
| `theme` | `'light' \| 'dark'` | Theme mode (default: 'light') |
|
|
1201
|
+
| `themeOverrides` | `Partial<CedrosThemeTokens>` | Custom theme token overrides |
|
|
1202
|
+
| `unstyled` | `boolean` | Disable all default styles (default: false) |
|
|
1203
|
+
| `solanaEndpoint` | `string` | Custom Solana RPC endpoint |
|
|
1204
|
+
| `tokenMint` | `string` | SPL token mint address (default: USDC) - see [Token Mint Validation](#-token-mint-validation) |
|
|
1205
|
+
| `dangerouslyAllowUnknownMint` | `boolean` | Allow unknown token mints (default: false) - ⚠️ WARNING: Only enable after triple-checking mint address - see [Token Mint Validation](#-token-mint-validation) |
|
|
1206
|
+
| `logLevel` | `LogLevel` | Logging verbosity (default: `LogLevel.WARN` in production, `LogLevel.DEBUG` in development) - see [Logging](#-logging) |
|
|
1207
|
+
|
|
1208
|
+
### CedrosPay Component
|
|
1209
|
+
|
|
1210
|
+
| Prop | Type | Description |
|
|
1211
|
+
| ----------- | ----------------- | ------------------------------------------------------------ |
|
|
1212
|
+
| `resource` | `string` | Single resource ID (use this OR items) |
|
|
1213
|
+
| `items` | `CartItem[]` | Array of cart items (use this OR resource) |
|
|
1214
|
+
| `checkout` | `CheckoutOptions` | Customer email, coupons, redirects, metadata |
|
|
1215
|
+
| `display` | `DisplayOptions` | Labels, visibility (showCard, showCrypto), layout, className |
|
|
1216
|
+
| `callbacks` | `CallbackOptions` | onPaymentSuccess, onPaymentError, onPaymentAttempt |
|
|
1217
|
+
| `advanced` | `AdvancedOptions` | Custom wallets, autoDetectWallets, testPageUrl |
|
|
1218
|
+
|
|
1219
|
+
#### Checkout Options
|
|
1220
|
+
|
|
1221
|
+
| Field | Type | Description |
|
|
1222
|
+
| --------------- | ------------------------ | ---------------------------------- |
|
|
1223
|
+
| `customerEmail` | `string` | Pre-fill email for Stripe checkout |
|
|
1224
|
+
| `couponCode` | `string` | Coupon code to apply |
|
|
1225
|
+
| `successUrl` | `string` | Stripe redirect URL on success |
|
|
1226
|
+
| `cancelUrl` | `string` | Stripe redirect URL on cancel |
|
|
1227
|
+
| `metadata` | `Record<string, string>` | Custom tracking data |
|
|
1228
|
+
|
|
1229
|
+
#### Display Options
|
|
1230
|
+
|
|
1231
|
+
| Field | Type | Description |
|
|
1232
|
+
| ------------- | ---------------------------- | ----------------------------------- |
|
|
1233
|
+
| `cardLabel` | `string` | Stripe button label |
|
|
1234
|
+
| `cryptoLabel` | `string` | Crypto button label |
|
|
1235
|
+
| `showCard` | `boolean` | Show Stripe button (default: true) |
|
|
1236
|
+
| `showCrypto` | `boolean` | Show crypto button (default: true) |
|
|
1237
|
+
| `layout` | `'vertical' \| 'horizontal'` | Button layout (default: 'vertical') |
|
|
1238
|
+
| `className` | `string` | Custom CSS class |
|
|
1239
|
+
|
|
1240
|
+
#### Callback Options
|
|
1241
|
+
|
|
1242
|
+
| Field | Type | Description |
|
|
1243
|
+
| ------------------ | ---------------------------------------- | ---------------------------- |
|
|
1244
|
+
| `onPaymentSuccess` | `(result: PaymentSuccessResult) => void` | Called on successful payment |
|
|
1245
|
+
| `onPaymentError` | `(error: PaymentErrorDetail) => void` | Called on payment error |
|
|
1246
|
+
| `onPaymentAttempt` | `(method: 'stripe' \| 'crypto') => void` | Called when payment starts |
|
|
1247
|
+
|
|
1248
|
+
[Full API Reference →](https://github.com/CedrosPay/react/tree/main/stories)
|
|
1249
|
+
|
|
1250
|
+
---
|
|
1251
|
+
|
|
1252
|
+
## ⚠️ Token Mint Validation
|
|
1253
|
+
|
|
1254
|
+
**CRITICAL:** Typos in token mint addresses result in payments being sent to the wrong token, causing **permanent loss of funds**.
|
|
1255
|
+
|
|
1256
|
+
Cedros Pay includes **strict validation** against known stablecoin addresses to prevent catastrophic misconfigurations. If you specify a `tokenMint` that doesn't match a known stablecoin, **initialization will fail with an error**.
|
|
1257
|
+
|
|
1258
|
+
### Known Stablecoins (mainnet-beta)
|
|
1259
|
+
|
|
1260
|
+
| Symbol | Mint Address |
|
|
1261
|
+
| ------ | ---------------------------------------------- |
|
|
1262
|
+
| USDC | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` |
|
|
1263
|
+
| USDT | `Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB` |
|
|
1264
|
+
| PYUSD | `2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo` |
|
|
1265
|
+
| CASH | `CASHx9KJUStyftLFWGvEVf59SGeG9sh5FfcnZMVPCASH` |
|
|
1266
|
+
|
|
1267
|
+
### Strict Mode (Default)
|
|
1268
|
+
|
|
1269
|
+
By default, unknown token mints throw an error:
|
|
1270
|
+
|
|
1271
|
+
```tsx
|
|
1272
|
+
<CedrosProvider
|
|
1273
|
+
config={{
|
|
1274
|
+
stripePublicKey: "pk_test_...",
|
|
1275
|
+
serverUrl: window.location.origin,
|
|
1276
|
+
solanaCluster: "mainnet-beta",
|
|
1277
|
+
tokenMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" // ✅ USDC - works
|
|
1278
|
+
}}
|
|
1279
|
+
>
|
|
1280
|
+
{/* ... */}
|
|
1281
|
+
</CedrosProvider>
|
|
1282
|
+
|
|
1283
|
+
// Using an unknown token mint throws an error:
|
|
1284
|
+
<CedrosProvider
|
|
1285
|
+
config={{
|
|
1286
|
+
stripePublicKey: "pk_test_...",
|
|
1287
|
+
serverUrl: window.location.origin,
|
|
1288
|
+
solanaCluster: "devnet",
|
|
1289
|
+
tokenMint: "CustomTokenMint123..." // ❌ Throws error: SAFETY ERROR
|
|
1290
|
+
}}
|
|
1291
|
+
>
|
|
1292
|
+
{/* ... */}
|
|
1293
|
+
</CedrosProvider>
|
|
1294
|
+
```
|
|
1295
|
+
|
|
1296
|
+
**Error Message:**
|
|
1297
|
+
|
|
1298
|
+
```
|
|
1299
|
+
SAFETY ERROR: Unrecognized token mint address in CedrosConfig.tokenMint
|
|
1300
|
+
Provided: CustomTokenMint123...
|
|
1301
|
+
|
|
1302
|
+
This token mint does not match any known stablecoin addresses.
|
|
1303
|
+
Using an unknown token mint can result in PERMANENT LOSS OF FUNDS if it's a typo.
|
|
1304
|
+
|
|
1305
|
+
Known stablecoin mints (mainnet-beta):
|
|
1306
|
+
USDC: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
|
|
1307
|
+
USDT: Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB
|
|
1308
|
+
PYUSD: 2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo
|
|
1309
|
+
CASH: CASHx9KJUStyftLFWGvEVf59SGeG9sh5FfcnZMVPCASH
|
|
1310
|
+
|
|
1311
|
+
If you are CERTAIN this is the correct mint address (custom token, testnet, or new stablecoin),
|
|
1312
|
+
set dangerouslyAllowUnknownMint={true} in your CedrosProvider config.
|
|
1313
|
+
```
|
|
1314
|
+
|
|
1315
|
+
### Permissive Mode (Opt-in)
|
|
1316
|
+
|
|
1317
|
+
For custom tokens, testnet tokens, or new stablecoins, you must explicitly opt-in:
|
|
1318
|
+
|
|
1319
|
+
```tsx
|
|
1320
|
+
<CedrosProvider
|
|
1321
|
+
config={{
|
|
1322
|
+
stripePublicKey: "pk_test_...",
|
|
1323
|
+
serverUrl: window.location.origin,
|
|
1324
|
+
solanaCluster: "devnet",
|
|
1325
|
+
tokenMint: "CustomTokenMint123...", // Custom token
|
|
1326
|
+
dangerouslyAllowUnknownMint: true, // ⚠️ Explicit opt-in required
|
|
1327
|
+
}}
|
|
1328
|
+
>
|
|
1329
|
+
{/* ... */}
|
|
1330
|
+
</CedrosProvider>
|
|
1331
|
+
```
|
|
1332
|
+
|
|
1333
|
+
**⚠️ WARNING:** Only enable `dangerouslyAllowUnknownMint` if you have **TRIPLE-CHECKED** the mint address. A typo will result in permanent loss of funds.
|
|
1334
|
+
|
|
1335
|
+
### Validation Points
|
|
1336
|
+
|
|
1337
|
+
Strict validation runs at **three points** to protect against fund loss:
|
|
1338
|
+
|
|
1339
|
+
1. **Config initialization** - When `<CedrosProvider>` mounts
|
|
1340
|
+
2. **Payment quote** - When backend returns x402 quote with `asset` field
|
|
1341
|
+
3. **Runtime** - When building Solana transactions
|
|
1342
|
+
|
|
1343
|
+
**Best Practices:**
|
|
1344
|
+
|
|
1345
|
+
1. ✅ Use known stablecoin mints in production (USDC, USDT, PYUSD, CASH)
|
|
1346
|
+
2. ✅ Triple-check any custom mint addresses before enabling `dangerouslyAllowUnknownMint`
|
|
1347
|
+
3. ✅ Test thoroughly on devnet before deploying to mainnet
|
|
1348
|
+
4. ❌ Never copy-paste mint addresses without verification
|
|
1349
|
+
5. ❌ Never use `dangerouslyAllowUnknownMint` unless absolutely necessary
|
|
1350
|
+
|
|
1351
|
+
---
|
|
1352
|
+
|
|
1353
|
+
## 📊 Logging
|
|
1354
|
+
|
|
1355
|
+
Cedros Pay includes structured logging with configurable log levels to control verbosity and keep production logs clean.
|
|
1356
|
+
|
|
1357
|
+
### Log Levels
|
|
1358
|
+
|
|
1359
|
+
```typescript
|
|
1360
|
+
import { LogLevel } from "@cedros/pay-react";
|
|
1361
|
+
|
|
1362
|
+
export enum LogLevel {
|
|
1363
|
+
DEBUG = 0, // Detailed debug information (verbose)
|
|
1364
|
+
INFO = 1, // Informational messages
|
|
1365
|
+
WARN = 2, // Warnings and potentially problematic situations
|
|
1366
|
+
ERROR = 3, // Error messages only
|
|
1367
|
+
SILENT = 4, // No logging
|
|
1368
|
+
}
|
|
1369
|
+
```
|
|
1370
|
+
|
|
1371
|
+
### Default Behavior
|
|
1372
|
+
|
|
1373
|
+
- **Development:** `LogLevel.DEBUG` (show all logs)
|
|
1374
|
+
- **Production:** `LogLevel.WARN` (warnings and errors only)
|
|
1375
|
+
|
|
1376
|
+
### Configuration
|
|
1377
|
+
|
|
1378
|
+
Control logging verbosity via the `logLevel` prop:
|
|
1379
|
+
|
|
1380
|
+
```typescript
|
|
1381
|
+
import { CedrosProvider, LogLevel } from '@cedros/pay-react';
|
|
1382
|
+
|
|
1383
|
+
// Production: Only show errors
|
|
1384
|
+
<CedrosProvider
|
|
1385
|
+
config={{
|
|
1386
|
+
stripePublicKey: "pk_live_...",
|
|
1387
|
+
serverUrl: window.location.origin,
|
|
1388
|
+
solanaCluster: "mainnet-beta",
|
|
1389
|
+
logLevel: LogLevel.ERROR
|
|
1390
|
+
}}
|
|
1391
|
+
>
|
|
1392
|
+
<App />
|
|
1393
|
+
</CedrosProvider>
|
|
1394
|
+
|
|
1395
|
+
// Development: Show all logs (default)
|
|
1396
|
+
<CedrosProvider
|
|
1397
|
+
config={{
|
|
1398
|
+
stripePublicKey: "pk_test_...",
|
|
1399
|
+
serverUrl: window.location.origin,
|
|
1400
|
+
solanaCluster: "devnet",
|
|
1401
|
+
logLevel: LogLevel.DEBUG
|
|
1402
|
+
}}
|
|
1403
|
+
>
|
|
1404
|
+
<App />
|
|
1405
|
+
</CedrosProvider>
|
|
1406
|
+
|
|
1407
|
+
// CI/Testing: Silence all logs
|
|
1408
|
+
<CedrosProvider
|
|
1409
|
+
config={{
|
|
1410
|
+
stripePublicKey: "pk_test_...",
|
|
1411
|
+
serverUrl: window.location.origin,
|
|
1412
|
+
solanaCluster: "devnet",
|
|
1413
|
+
logLevel: LogLevel.SILENT
|
|
1414
|
+
}}
|
|
1415
|
+
>
|
|
1416
|
+
<App />
|
|
1417
|
+
</CedrosProvider>
|
|
1418
|
+
```
|
|
1419
|
+
|
|
1420
|
+
### Advanced Usage
|
|
1421
|
+
|
|
1422
|
+
For custom logging or integration with your logging infrastructure:
|
|
1423
|
+
|
|
1424
|
+
```typescript
|
|
1425
|
+
import { createLogger, LogLevel } from "@cedros/pay-react";
|
|
1426
|
+
|
|
1427
|
+
// Create a custom logger instance
|
|
1428
|
+
const logger = createLogger({
|
|
1429
|
+
level: LogLevel.INFO,
|
|
1430
|
+
prefix: "[MyApp]", // Optional prefix for all logs
|
|
1431
|
+
});
|
|
1432
|
+
|
|
1433
|
+
// Use directly
|
|
1434
|
+
logger.debug("Debug message");
|
|
1435
|
+
logger.info("Info message");
|
|
1436
|
+
logger.warn("Warning message");
|
|
1437
|
+
logger.error("Error message");
|
|
1438
|
+
|
|
1439
|
+
// Update log level dynamically
|
|
1440
|
+
logger.setLevel(LogLevel.ERROR);
|
|
1441
|
+
```
|
|
1442
|
+
|
|
1443
|
+
### Log Format
|
|
1444
|
+
|
|
1445
|
+
All logs include timestamps and severity levels:
|
|
1446
|
+
|
|
1447
|
+
```
|
|
1448
|
+
[2025-11-09T10:43:12.345Z] [CedrosPay] [WARN] Token mint validation warning...
|
|
1449
|
+
[2025-11-09T10:43:15.678Z] [CedrosPay] [ERROR] Payment verification failed
|
|
1450
|
+
```
|
|
1451
|
+
|
|
1452
|
+
### Best Practices
|
|
1453
|
+
|
|
1454
|
+
1. **Production:** Use `LogLevel.ERROR` or `LogLevel.WARN` to avoid exposing sensitive data
|
|
1455
|
+
2. **Development:** Use `LogLevel.DEBUG` to troubleshoot payment flows
|
|
1456
|
+
3. **CI/Testing:** Use `LogLevel.SILENT` to keep test output clean
|
|
1457
|
+
4. **Monitoring:** Integrate with your logging infrastructure (Datadog, Sentry, etc.)
|
|
1458
|
+
|
|
1459
|
+
---
|
|
1460
|
+
|
|
1461
|
+
## 🪄 Example Use Cases
|
|
1462
|
+
|
|
1463
|
+
- Paywalled blog or API monetization
|
|
1464
|
+
- Agent-to-agent microtransactions
|
|
1465
|
+
- Subscription and one-time digital content unlocks
|
|
1466
|
+
- AI service pay-per-call endpoints
|
|
1467
|
+
|
|
1468
|
+
---
|
|
1469
|
+
|
|
1470
|
+
## 🔒 API Stability & Versioning
|
|
1471
|
+
|
|
1472
|
+
Cedros Pay is designed for enterprise use with a strong commitment to API stability.
|
|
1473
|
+
|
|
1474
|
+
### Semantic Versioning
|
|
1475
|
+
|
|
1476
|
+
We follow [Semantic Versioning](https://semver.org/):
|
|
1477
|
+
|
|
1478
|
+
- **Major (x.0.0)**: Breaking changes, API removals
|
|
1479
|
+
- **Minor (0.x.0)**: New features, backwards-compatible additions
|
|
1480
|
+
- **Patch (0.0.x)**: Bug fixes, no API changes
|
|
1481
|
+
|
|
1482
|
+
### Stable API Surface
|
|
1483
|
+
|
|
1484
|
+
**These exports are guaranteed stable** and follow semantic versioning:
|
|
1485
|
+
|
|
1486
|
+
- ✅ **Components** - All exported React components (CedrosPay, StripeButton, CryptoButton, etc.)
|
|
1487
|
+
- ✅ **Hooks** - useCedrosContext, useStripeCheckout, useX402Payment, etc.
|
|
1488
|
+
- ✅ **Manager Interfaces** - IStripeManager, IX402Manager, IWalletManager, IRouteDiscoveryManager
|
|
1489
|
+
- ✅ **Types** - All types exported via versioned namespaces (v1, v2, etc.)
|
|
1490
|
+
- ✅ **Utilities** - validateConfig, parseCouponCodes, rate limiters, logging, events
|
|
1491
|
+
|
|
1492
|
+
**Use interfaces, not concrete classes:**
|
|
1493
|
+
|
|
1494
|
+
```typescript
|
|
1495
|
+
// ✅ CORRECT: Use interface from context
|
|
1496
|
+
import { useCedrosContext } from '@cedros/pay-react';
|
|
1497
|
+
|
|
1498
|
+
function MyComponent() {
|
|
1499
|
+
const { stripeManager } = useCedrosContext();
|
|
1500
|
+
// stripeManager is typed as IStripeManager (stable)
|
|
1501
|
+
await stripeManager.processPayment({ ... });
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
// ❌ WRONG: Direct class import (unsupported)
|
|
1505
|
+
import { StripeManager } from '@cedros/pay-react'; // Not exported
|
|
1506
|
+
const manager = new StripeManager(...); // Will break
|
|
1507
|
+
```
|
|
1508
|
+
|
|
1509
|
+
### Deprecation Process
|
|
1510
|
+
|
|
1511
|
+
When APIs are deprecated:
|
|
1512
|
+
|
|
1513
|
+
1. **Deprecation Notice** - Warning logged, replacement documented
|
|
1514
|
+
2. **Minimum 3 months** - Grace period for migration
|
|
1515
|
+
3. **Migration Guide** - Step-by-step upgrade instructions
|
|
1516
|
+
4. **Major Version** - Removal in next major release only
|
|
1517
|
+
|
|
1518
|
+
**Example Timeline:**
|
|
1519
|
+
|
|
1520
|
+
- v2.1.0: Deprecate oldAPI, introduce newAPI
|
|
1521
|
+
- v2.2.0 - v2.x: Both supported, warnings logged
|
|
1522
|
+
- v3.0.0: Remove oldAPI, only newAPI available
|
|
1523
|
+
|
|
1524
|
+
### Type Versioning
|
|
1525
|
+
|
|
1526
|
+
Types use versioned namespaces to prevent breaking changes:
|
|
1527
|
+
|
|
1528
|
+
```typescript
|
|
1529
|
+
// Top-level exports (current stable version)
|
|
1530
|
+
import { X402Requirement } from '@cedros/pay-react';
|
|
1531
|
+
|
|
1532
|
+
// Explicit version (locks to v1, won't break on v2)
|
|
1533
|
+
import { v1 } from '@cedros/pay-react';
|
|
1534
|
+
const req: v1.X402Requirement = { ... };
|
|
1535
|
+
|
|
1536
|
+
// Future version
|
|
1537
|
+
import { v2 } from '@cedros/pay-react';
|
|
1538
|
+
const newReq: v2.X402Requirement = { ... };
|
|
1539
|
+
```
|
|
1540
|
+
|
|
1541
|
+
**Read more:** See [API_STABILITY.md](./API_STABILITY.md) for our complete stability policy.
|
|
1542
|
+
|
|
1543
|
+
---
|
|
1544
|
+
|
|
1545
|
+
## 📡 Error Telemetry (Optional)
|
|
1546
|
+
|
|
1547
|
+
Cedros Pay includes **opt-in error telemetry** with correlation IDs for production debugging. Telemetry is **disabled by default** and requires explicit configuration.
|
|
1548
|
+
|
|
1549
|
+
### Privacy-First Design
|
|
1550
|
+
|
|
1551
|
+
- ✅ **Opt-in only** - No data sent without your explicit configuration
|
|
1552
|
+
- ✅ **User-controlled** - You choose what service to use (Sentry, Datadog, custom, or none)
|
|
1553
|
+
- ✅ **PII sanitization** - Private keys, wallet addresses, emails automatically redacted
|
|
1554
|
+
- ✅ **No hidden network calls** - Data only sent via your callback function
|
|
1555
|
+
|
|
1556
|
+
### Quick Start
|
|
1557
|
+
|
|
1558
|
+
```typescript
|
|
1559
|
+
import { configureTelemetry, ErrorSeverity } from "@cedros/pay-react";
|
|
1560
|
+
import * as Sentry from "@sentry/react";
|
|
1561
|
+
|
|
1562
|
+
// Enable telemetry with Sentry
|
|
1563
|
+
configureTelemetry({
|
|
1564
|
+
enabled: true,
|
|
1565
|
+
sdkVersion: "2.0.0",
|
|
1566
|
+
environment: process.env.NODE_ENV,
|
|
1567
|
+
sanitizePII: true, // ALWAYS keep enabled
|
|
1568
|
+
onError: (error) => {
|
|
1569
|
+
Sentry.captureException(error.error, {
|
|
1570
|
+
extra: {
|
|
1571
|
+
correlationId: error.correlationId,
|
|
1572
|
+
paymentContext: error.paymentContext,
|
|
1573
|
+
},
|
|
1574
|
+
tags: error.tags,
|
|
1575
|
+
level: error.severity,
|
|
1576
|
+
});
|
|
1577
|
+
},
|
|
1578
|
+
});
|
|
1579
|
+
```
|
|
1580
|
+
|
|
1581
|
+
### Features
|
|
1582
|
+
|
|
1583
|
+
- **Correlation IDs** - Track errors across distributed systems
|
|
1584
|
+
- **Error Enrichment** - Add payment context (method, stage, amount) without PII
|
|
1585
|
+
- **PII Sanitization** - 15+ patterns including:
|
|
1586
|
+
- Private keys (Solana, Ethereum)
|
|
1587
|
+
- Wallet addresses
|
|
1588
|
+
- Seed phrases
|
|
1589
|
+
- API keys, JWT tokens
|
|
1590
|
+
- Credit cards, emails, phone numbers
|
|
1591
|
+
- **Integration Examples** - Sentry, Datadog, custom backends
|
|
1592
|
+
|
|
1593
|
+
### Security Guarantees
|
|
1594
|
+
|
|
1595
|
+
```typescript
|
|
1596
|
+
/**
|
|
1597
|
+
* SECURITY GUARANTEE:
|
|
1598
|
+
* - NEVER logs private keys, seed phrases, or wallet credentials
|
|
1599
|
+
* - NEVER sends data without explicit user configuration
|
|
1600
|
+
* - Sanitization ENABLED BY DEFAULT and cannot be fully disabled
|
|
1601
|
+
* - All sensitive crypto data patterns are redacted automatically
|
|
1602
|
+
*/
|
|
1603
|
+
```
|
|
1604
|
+
|
|
1605
|
+
### Correlation IDs for Support
|
|
1606
|
+
|
|
1607
|
+
```typescript
|
|
1608
|
+
import { generateCorrelationId } from "@cedros/pay-react";
|
|
1609
|
+
|
|
1610
|
+
function PaymentButton() {
|
|
1611
|
+
const [correlationId] = useState(generateCorrelationId());
|
|
1612
|
+
|
|
1613
|
+
const handleError = (error: Error) => {
|
|
1614
|
+
reportError(error, { correlationId });
|
|
1615
|
+
|
|
1616
|
+
// Show correlation ID to user for support
|
|
1617
|
+
alert(`Payment failed. Support ID: ${correlationId}`);
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
```
|
|
1621
|
+
|
|
1622
|
+
**Full Integration Guide:** See [TELEMETRY_INTEGRATIONS.md](./TELEMETRY_INTEGRATIONS.md) for complete examples with Sentry, Datadog, and custom backends.
|
|
1623
|
+
|
|
1624
|
+
---
|
|
1625
|
+
|
|
1626
|
+
## 🧭 Roadmap
|
|
1627
|
+
|
|
1628
|
+
- Stripe + x402 unified dashboard
|
|
1629
|
+
- Subscription management
|
|
1630
|
+
- Facilitator integrations (PayAI, Kora)
|
|
1631
|
+
- Typed SDKs for Go, Node, and Python agents
|
|
1632
|
+
|
|
1633
|
+
---
|
|
1634
|
+
|
|
1635
|
+
**Cedros Pay** — _rooted in Solana, built for the web._
|
|
1636
|
+
|
|
1637
|
+
## 🎨 Storybook Development
|
|
1638
|
+
|
|
1639
|
+
### Setup
|
|
1640
|
+
|
|
1641
|
+
1. **Copy the environment template:**
|
|
1642
|
+
|
|
1643
|
+
```bash
|
|
1644
|
+
cp .env.example .env
|
|
1645
|
+
```
|
|
1646
|
+
|
|
1647
|
+
2. **Configure your credentials:**
|
|
1648
|
+
|
|
1649
|
+
Edit `.env` and add your keys:
|
|
1650
|
+
|
|
1651
|
+
```bash
|
|
1652
|
+
# Stripe test key (required for card payments)
|
|
1653
|
+
VITE_STRIPE_PUBLIC_KEY=pk_test_your_key_here
|
|
1654
|
+
|
|
1655
|
+
# Solana RPC endpoint (required for crypto payments)
|
|
1656
|
+
VITE_SOLANA_RPC_URL=https://your-rpc-endpoint/
|
|
1657
|
+
|
|
1658
|
+
# Backend server URL
|
|
1659
|
+
VITE_SERVER_URL=http://localhost:8080
|
|
1660
|
+
```
|
|
1661
|
+
|
|
1662
|
+
**⚠️ Required for testing:**
|
|
1663
|
+
|
|
1664
|
+
- **Stripe:** Get a test key from [Stripe Dashboard](https://dashboard.stripe.com/test/apikeys)
|
|
1665
|
+
- **Solana RPC:** Public RPCs have strict rate limits. Get a free endpoint from:
|
|
1666
|
+
- [Helius](https://www.helius.dev/) (recommended)
|
|
1667
|
+
- [QuickNode](https://www.quicknode.com/)
|
|
1668
|
+
- [Alchemy](https://www.alchemy.com/)
|
|
1669
|
+
|
|
1670
|
+
3. **Run Storybook:**
|
|
1671
|
+
```bash
|
|
1672
|
+
npm run storybook
|
|
1673
|
+
```
|
|
1674
|
+
|
|
1675
|
+
### Troubleshooting
|
|
1676
|
+
|
|
1677
|
+
**Error: "Endpoint URL must start with `http:` or `https:`"**
|
|
1678
|
+
|
|
1679
|
+
This means `VITE_SOLANA_RPC_URL` or `VITE_STORYBOOK_SOLANA_ENDPOINT` is missing or empty in your `.env` file.
|
|
1680
|
+
|
|
1681
|
+
**Fix:**
|
|
1682
|
+
```bash
|
|
1683
|
+
# Add to .env
|
|
1684
|
+
VITE_SOLANA_RPC_URL=https://api.devnet.solana.com
|
|
1685
|
+
# Or for Storybook-specific override:
|
|
1686
|
+
VITE_STORYBOOK_SOLANA_ENDPOINT=https://api.devnet.solana.com
|
|
1687
|
+
```
|
|
1688
|
+
|
|
1689
|
+
**Error: "Invalid Cedros configuration: serverUrl must be a non-empty string"**
|
|
1690
|
+
|
|
1691
|
+
The `VITE_SERVER_URL` or `VITE_STORYBOOK_SERVER_URL` is missing.
|
|
1692
|
+
|
|
1693
|
+
**Fix:**
|
|
1694
|
+
```bash
|
|
1695
|
+
# Add to .env
|
|
1696
|
+
VITE_SERVER_URL=http://localhost:8080
|
|
1697
|
+
```
|
|
1698
|
+
|
|
1699
|
+
### Environment Variables
|
|
1700
|
+
|
|
1701
|
+
| Variable | Description | Required |
|
|
1702
|
+
| -------------------------------- | --------------------------------- | ------------------------------------- |
|
|
1703
|
+
| `VITE_STRIPE_PUBLIC_KEY` | Stripe publishable key | ✅ For card payments |
|
|
1704
|
+
| `VITE_SERVER_URL` | Backend API endpoint | ✅ (default: `http://localhost:8080`) |
|
|
1705
|
+
| `VITE_SOLANA_CLUSTER` | Solana network | Optional (default: `mainnet-beta`) |
|
|
1706
|
+
| `VITE_SOLANA_RPC_URL` | Custom Solana RPC | ✅ For crypto payments |
|
|
1707
|
+
| `VITE_STORYBOOK_SERVER_URL` | Override server URL for Storybook | Optional |
|
|
1708
|
+
| `VITE_STORYBOOK_SOLANA_ENDPOINT` | Override RPC for Storybook | Optional |
|
|
1709
|
+
|
|
1710
|
+
## 🤝 Contributing
|
|
1711
|
+
|
|
1712
|
+
We welcome contributions! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines on:
|
|
1713
|
+
|
|
1714
|
+
- Development setup and workflow
|
|
1715
|
+
- Code standards and architecture principles
|
|
1716
|
+
- Testing requirements
|
|
1717
|
+
- PR submission process
|
|
1718
|
+
- Security guidelines
|
|
1719
|
+
|
|
1720
|
+
Before submitting a PR, make sure all tests pass:
|
|
1721
|
+
|
|
1722
|
+
```bash
|
|
1723
|
+
npm run lint
|
|
1724
|
+
npm run type-check
|
|
1725
|
+
npm test
|
|
1726
|
+
npm run test:coverage
|
|
1727
|
+
```
|
|
1728
|
+
|
|
1729
|
+
## 📄 License
|
|
1730
|
+
|
|
1731
|
+
MIT License - see [LICENSE](./LICENSE) for details.
|