@stripe-sdk/core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +700 -0
- package/dist/client/index.d.mts +198 -0
- package/dist/client/index.d.ts +198 -0
- package/dist/client/index.js +517 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/index.mjs +506 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/index-D8rM_YD4.d.mts +375 -0
- package/dist/index-D8rM_YD4.d.ts +375 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1800 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1708 -0
- package/dist/index.mjs.map +1 -0
- package/dist/next/index.d.mts +65 -0
- package/dist/next/index.d.ts +65 -0
- package/dist/next/index.js +450 -0
- package/dist/next/index.js.map +1 -0
- package/dist/next/index.mjs +443 -0
- package/dist/next/index.mjs.map +1 -0
- package/dist/server/index.d.mts +115 -0
- package/dist/server/index.d.ts +115 -0
- package/dist/server/index.js +1290 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/index.mjs +1208 -0
- package/dist/server/index.mjs.map +1 -0
- package/dist/server/webhooks/index.d.mts +2 -0
- package/dist/server/webhooks/index.d.ts +2 -0
- package/dist/server/webhooks/index.js +156 -0
- package/dist/server/webhooks/index.js.map +1 -0
- package/dist/server/webhooks/index.mjs +152 -0
- package/dist/server/webhooks/index.mjs.map +1 -0
- package/package.json +109 -0
package/README.md
ADDED
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
# @stripe-sdk/core
|
|
2
|
+
|
|
3
|
+
SDK Stripe **cle en main** pour vos applications. Integrez les paiements, abonnements, factures, webhooks, marketplace et plus en quelques lignes. Compatible React, Next.js, Vue, Angular et tout framework JavaScript.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @stripe-sdk/core stripe @stripe/stripe-js @stripe/react-stripe-js
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Configuration
|
|
12
|
+
|
|
13
|
+
### 1. Variables d'environnement
|
|
14
|
+
|
|
15
|
+
```env
|
|
16
|
+
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
|
|
17
|
+
STRIPE_SECRET_KEY=sk_test_...
|
|
18
|
+
STRIPE_WEBHOOK_SECRET=whsec_...
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### 2. Initialisation (serveur)
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// lib/stripe.ts
|
|
25
|
+
import { initStripe } from '@stripe-sdk/core/server';
|
|
26
|
+
|
|
27
|
+
export const stripe = initStripe({
|
|
28
|
+
secretKey: process.env.STRIPE_SECRET_KEY!,
|
|
29
|
+
publishableKey: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!,
|
|
30
|
+
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Paiements
|
|
37
|
+
|
|
38
|
+
### Paiement simple avec formulaire integre
|
|
39
|
+
|
|
40
|
+
**Serveur** - Creer un PaymentIntent :
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
// app/api/create-payment/route.ts
|
|
44
|
+
import '@/lib/stripe'; // initialise Stripe
|
|
45
|
+
import { createPaymentIntent } from '@stripe-sdk/core/server';
|
|
46
|
+
|
|
47
|
+
export async function POST(req: Request) {
|
|
48
|
+
const { amount } = await req.json();
|
|
49
|
+
|
|
50
|
+
const result = await createPaymentIntent({
|
|
51
|
+
amount, // en centimes (1000 = 10.00 EUR)
|
|
52
|
+
currency: 'eur',
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (result.error) {
|
|
56
|
+
return Response.json(result.error, { status: 400 });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return Response.json({ clientSecret: result.data.client_secret });
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Client** - Formulaire de paiement :
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
// app/checkout/page.tsx
|
|
67
|
+
'use client';
|
|
68
|
+
import { StripeElementsProvider, CheckoutForm } from '@stripe-sdk/core/client';
|
|
69
|
+
|
|
70
|
+
export default function CheckoutPage() {
|
|
71
|
+
const [clientSecret, setClientSecret] = useState('');
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
fetch('/api/create-payment', {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
body: JSON.stringify({ amount: 2000 }), // 20 EUR
|
|
77
|
+
})
|
|
78
|
+
.then(r => r.json())
|
|
79
|
+
.then(data => setClientSecret(data.clientSecret));
|
|
80
|
+
}, []);
|
|
81
|
+
|
|
82
|
+
if (!clientSecret) return <p>Loading...</p>;
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<StripeElementsProvider
|
|
86
|
+
publishableKey={process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!}
|
|
87
|
+
clientSecret={clientSecret}
|
|
88
|
+
>
|
|
89
|
+
<CheckoutForm
|
|
90
|
+
onSuccess={(id) => console.log('Paid!', id)}
|
|
91
|
+
onError={(err) => console.error(err)}
|
|
92
|
+
submitLabel="Payer 20 EUR"
|
|
93
|
+
showEmail
|
|
94
|
+
/>
|
|
95
|
+
</StripeElementsProvider>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Checkout Session (page Stripe hebergee)
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
// Server
|
|
104
|
+
import { createCheckoutSession } from '@stripe-sdk/core/server';
|
|
105
|
+
|
|
106
|
+
const result = await createCheckoutSession({
|
|
107
|
+
mode: 'payment',
|
|
108
|
+
lineItems: [{ priceId: 'price_xxx', quantity: 1 }],
|
|
109
|
+
successUrl: 'https://monsite.com/success?session_id={CHECKOUT_SESSION_ID}',
|
|
110
|
+
cancelUrl: 'https://monsite.com/cancel',
|
|
111
|
+
automaticTax: true,
|
|
112
|
+
allowPromotionCodes: true,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Redirect le client vers result.data.url
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
// Client - redirect vers Checkout
|
|
120
|
+
import { useCheckout } from '@stripe-sdk/core/client';
|
|
121
|
+
|
|
122
|
+
function BuyButton() {
|
|
123
|
+
const { redirectToCheckout, isLoading } = useCheckout({
|
|
124
|
+
publishableKey: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const handleClick = async () => {
|
|
128
|
+
const res = await fetch('/api/create-checkout-session', { method: 'POST' });
|
|
129
|
+
const { sessionId } = await res.json();
|
|
130
|
+
await redirectToCheckout(sessionId);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
return <button onClick={handleClick} disabled={isLoading}>Acheter</button>;
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Payment Link (sans code)
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { createPaymentLink } from '@stripe-sdk/core/server';
|
|
141
|
+
|
|
142
|
+
const result = await createPaymentLink({
|
|
143
|
+
lineItems: [{ priceId: 'price_xxx', quantity: 1 }],
|
|
144
|
+
allowPromotionCodes: true,
|
|
145
|
+
afterCompletion: {
|
|
146
|
+
type: 'redirect',
|
|
147
|
+
redirectUrl: 'https://monsite.com/merci',
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
console.log(result.data.url); // Lien partageable
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Abonnements
|
|
157
|
+
|
|
158
|
+
### Creer un abonnement
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
import { createSubscription } from '@stripe-sdk/core/server';
|
|
162
|
+
|
|
163
|
+
const result = await createSubscription({
|
|
164
|
+
customerId: 'cus_xxx',
|
|
165
|
+
priceId: 'price_monthly_xxx',
|
|
166
|
+
trialPeriodDays: 14,
|
|
167
|
+
paymentBehavior: 'default_incomplete',
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// result.data.latest_invoice.payment_intent.client_secret
|
|
171
|
+
// -> utiliser avec StripeElementsProvider pour confirmer le paiement
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Changer de plan
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
import { updateSubscription } from '@stripe-sdk/core/server';
|
|
178
|
+
|
|
179
|
+
await updateSubscription({
|
|
180
|
+
subscriptionId: 'sub_xxx',
|
|
181
|
+
items: [{ id: 'si_xxx', priceId: 'price_annual_xxx' }],
|
|
182
|
+
prorationBehavior: 'create_prorations',
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Annuler / Reprendre
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
import { cancelSubscription, resumeSubscription } from '@stripe-sdk/core/server';
|
|
190
|
+
|
|
191
|
+
// Annuler a la fin de la periode
|
|
192
|
+
await cancelSubscription({
|
|
193
|
+
subscriptionId: 'sub_xxx',
|
|
194
|
+
cancelAtPeriodEnd: true,
|
|
195
|
+
cancellationDetails: { feedback: 'too_expensive' },
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Reprendre
|
|
199
|
+
await resumeSubscription('sub_xxx');
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Composant PricingTable
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
import { PricingTable, type PricingPlan } from '@stripe-sdk/core/client';
|
|
206
|
+
|
|
207
|
+
const plans: PricingPlan[] = [
|
|
208
|
+
{
|
|
209
|
+
id: 'starter',
|
|
210
|
+
name: 'Starter',
|
|
211
|
+
priceId: 'price_starter',
|
|
212
|
+
amount: 900,
|
|
213
|
+
currency: 'eur',
|
|
214
|
+
interval: 'month',
|
|
215
|
+
features: ['5 projets', '1 Go stockage', 'Support email'],
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: 'pro',
|
|
219
|
+
name: 'Pro',
|
|
220
|
+
priceId: 'price_pro',
|
|
221
|
+
amount: 2900,
|
|
222
|
+
currency: 'eur',
|
|
223
|
+
interval: 'month',
|
|
224
|
+
features: ['Projets illimites', '100 Go stockage', 'Support prioritaire', 'API access'],
|
|
225
|
+
highlighted: true,
|
|
226
|
+
badge: 'Populaire',
|
|
227
|
+
},
|
|
228
|
+
];
|
|
229
|
+
|
|
230
|
+
<PricingTable
|
|
231
|
+
plans={plans}
|
|
232
|
+
onSelectPlan={(plan) => handleSubscribe(plan.priceId)}
|
|
233
|
+
currentPlanId="starter"
|
|
234
|
+
/>
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Composant SubscriptionManager
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
import { SubscriptionManager } from '@stripe-sdk/core/client';
|
|
241
|
+
|
|
242
|
+
<SubscriptionManager
|
|
243
|
+
subscription={{
|
|
244
|
+
id: 'sub_xxx',
|
|
245
|
+
status: 'active',
|
|
246
|
+
planName: 'Pro',
|
|
247
|
+
amount: 2900,
|
|
248
|
+
currency: 'eur',
|
|
249
|
+
interval: 'month',
|
|
250
|
+
currentPeriodEnd: '2025-03-01',
|
|
251
|
+
cancelAtPeriodEnd: false,
|
|
252
|
+
}}
|
|
253
|
+
onCancel={async (id) => {
|
|
254
|
+
await fetch(`/api/subscriptions/${id}/cancel`, { method: 'POST' });
|
|
255
|
+
}}
|
|
256
|
+
onResume={async (id) => {
|
|
257
|
+
await fetch(`/api/subscriptions/${id}/resume`, { method: 'POST' });
|
|
258
|
+
}}
|
|
259
|
+
onManageBilling={() => window.location.href = '/api/portal'}
|
|
260
|
+
/>
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Clients
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
import {
|
|
269
|
+
createCustomer,
|
|
270
|
+
retrieveCustomer,
|
|
271
|
+
updateCustomer,
|
|
272
|
+
deleteCustomer,
|
|
273
|
+
listCustomers,
|
|
274
|
+
searchCustomers,
|
|
275
|
+
} from '@stripe-sdk/core/server';
|
|
276
|
+
|
|
277
|
+
// Creer
|
|
278
|
+
const { data: customer } = await createCustomer({
|
|
279
|
+
email: 'user@example.com',
|
|
280
|
+
name: 'John Doe',
|
|
281
|
+
metadata: { userId: 'usr_123' },
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// Rechercher
|
|
285
|
+
const { data: results } = await searchCustomers("email:'user@example.com'");
|
|
286
|
+
|
|
287
|
+
// Mettre a jour
|
|
288
|
+
await updateCustomer({
|
|
289
|
+
customerId: 'cus_xxx',
|
|
290
|
+
name: 'Jane Doe',
|
|
291
|
+
defaultPaymentMethodId: 'pm_xxx',
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Portail Client
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
import { createPortalSession } from '@stripe-sdk/core/server';
|
|
301
|
+
|
|
302
|
+
// API route
|
|
303
|
+
export async function POST(req: Request) {
|
|
304
|
+
const { customerId } = await req.json();
|
|
305
|
+
const result = await createPortalSession({
|
|
306
|
+
customerId,
|
|
307
|
+
returnUrl: 'https://monsite.com/account',
|
|
308
|
+
});
|
|
309
|
+
return Response.json({ url: result.data.url });
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Le client est redirige vers le portail Stripe pour gerer
|
|
313
|
+
// ses abonnements, moyens de paiement et factures.
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Sauvegarder un moyen de paiement (SetupIntent)
|
|
319
|
+
|
|
320
|
+
```ts
|
|
321
|
+
// Serveur
|
|
322
|
+
import { createSetupIntent } from '@stripe-sdk/core/server';
|
|
323
|
+
|
|
324
|
+
const result = await createSetupIntent({
|
|
325
|
+
customerId: 'cus_xxx',
|
|
326
|
+
usage: 'off_session', // pour les paiements futurs sans le client
|
|
327
|
+
});
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
```tsx
|
|
331
|
+
// Client
|
|
332
|
+
import { StripeElementsProvider, SetupForm } from '@stripe-sdk/core/client';
|
|
333
|
+
|
|
334
|
+
<StripeElementsProvider
|
|
335
|
+
publishableKey={process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!}
|
|
336
|
+
clientSecret={setupClientSecret}
|
|
337
|
+
>
|
|
338
|
+
<SetupForm
|
|
339
|
+
onSuccess={(setupId, pmId) => {
|
|
340
|
+
console.log('Saved!', pmId);
|
|
341
|
+
}}
|
|
342
|
+
submitLabel="Sauvegarder la carte"
|
|
343
|
+
/>
|
|
344
|
+
</StripeElementsProvider>
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## Produits & Prix
|
|
350
|
+
|
|
351
|
+
```ts
|
|
352
|
+
import {
|
|
353
|
+
createProduct,
|
|
354
|
+
createPrice,
|
|
355
|
+
listProducts,
|
|
356
|
+
listPrices,
|
|
357
|
+
} from '@stripe-sdk/core/server';
|
|
358
|
+
|
|
359
|
+
// Creer un produit avec prix par defaut
|
|
360
|
+
const { data: product } = await createProduct({
|
|
361
|
+
name: 'Plan Pro',
|
|
362
|
+
description: 'Acces complet a la plateforme',
|
|
363
|
+
defaultPriceData: {
|
|
364
|
+
unitAmount: 2900, // 29.00 EUR
|
|
365
|
+
currency: 'eur',
|
|
366
|
+
recurring: { interval: 'month' },
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// Ajouter un prix annuel
|
|
371
|
+
await createPrice({
|
|
372
|
+
productId: product.id,
|
|
373
|
+
unitAmount: 29000, // 290.00 EUR
|
|
374
|
+
currency: 'eur',
|
|
375
|
+
recurring: { interval: 'year' },
|
|
376
|
+
lookupKey: 'pro_annual',
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// Lister les produits actifs
|
|
380
|
+
const { data: products } = await listProducts({ active: true, limit: 20 });
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## Factures
|
|
386
|
+
|
|
387
|
+
```ts
|
|
388
|
+
import {
|
|
389
|
+
createInvoice,
|
|
390
|
+
createInvoiceItem,
|
|
391
|
+
finalizeInvoice,
|
|
392
|
+
sendInvoice,
|
|
393
|
+
listInvoices,
|
|
394
|
+
getUpcomingInvoice,
|
|
395
|
+
} from '@stripe-sdk/core/server';
|
|
396
|
+
|
|
397
|
+
// Creer et envoyer une facture
|
|
398
|
+
const { data: invoice } = await createInvoice({
|
|
399
|
+
customerId: 'cus_xxx',
|
|
400
|
+
collectionMethod: 'send_invoice',
|
|
401
|
+
daysUntilDue: 30,
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
await createInvoiceItem({
|
|
405
|
+
customerId: 'cus_xxx',
|
|
406
|
+
invoiceId: invoice.id,
|
|
407
|
+
amount: 5000, // 50.00 EUR
|
|
408
|
+
currency: 'eur',
|
|
409
|
+
description: 'Consulting - Janvier 2025',
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
await finalizeInvoice(invoice.id);
|
|
413
|
+
await sendInvoice(invoice.id);
|
|
414
|
+
|
|
415
|
+
// Prochaine facture d'un abonnement
|
|
416
|
+
const { data: upcoming } = await getUpcomingInvoice('cus_xxx', 'sub_xxx');
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## Remboursements & Litiges
|
|
422
|
+
|
|
423
|
+
```ts
|
|
424
|
+
import {
|
|
425
|
+
createRefund,
|
|
426
|
+
listRefunds,
|
|
427
|
+
retrieveDispute,
|
|
428
|
+
updateDispute,
|
|
429
|
+
} from '@stripe-sdk/core/server';
|
|
430
|
+
|
|
431
|
+
// Remboursement total
|
|
432
|
+
await createRefund({ paymentIntentId: 'pi_xxx' });
|
|
433
|
+
|
|
434
|
+
// Remboursement partiel
|
|
435
|
+
await createRefund({
|
|
436
|
+
paymentIntentId: 'pi_xxx',
|
|
437
|
+
amount: 500, // 5.00 EUR
|
|
438
|
+
reason: 'requested_by_customer',
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// Repondre a un litige
|
|
442
|
+
await updateDispute({
|
|
443
|
+
disputeId: 'dp_xxx',
|
|
444
|
+
evidence: {
|
|
445
|
+
customerName: 'John Doe',
|
|
446
|
+
productDescription: 'Abonnement SaaS mensuel',
|
|
447
|
+
serviceDocumentation: 'file_xxx',
|
|
448
|
+
},
|
|
449
|
+
submit: true,
|
|
450
|
+
});
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## Coupons & Codes Promo
|
|
456
|
+
|
|
457
|
+
```ts
|
|
458
|
+
import {
|
|
459
|
+
createCoupon,
|
|
460
|
+
createPromotionCode,
|
|
461
|
+
listPromotionCodes,
|
|
462
|
+
} from '@stripe-sdk/core/server';
|
|
463
|
+
|
|
464
|
+
// Coupon -20%
|
|
465
|
+
const { data: coupon } = await createCoupon({
|
|
466
|
+
percentOff: 20,
|
|
467
|
+
duration: 'repeating',
|
|
468
|
+
durationInMonths: 3,
|
|
469
|
+
name: 'Lancement 2025',
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
// Code promo public
|
|
473
|
+
await createPromotionCode({
|
|
474
|
+
couponId: coupon.id,
|
|
475
|
+
code: 'LAUNCH20',
|
|
476
|
+
maxRedemptions: 100,
|
|
477
|
+
restrictions: {
|
|
478
|
+
firstTimeTransaction: true,
|
|
479
|
+
},
|
|
480
|
+
});
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
## Stripe Connect (Marketplace)
|
|
486
|
+
|
|
487
|
+
```ts
|
|
488
|
+
import {
|
|
489
|
+
createConnectAccount,
|
|
490
|
+
createAccountLink,
|
|
491
|
+
createTransfer,
|
|
492
|
+
getBalance,
|
|
493
|
+
} from '@stripe-sdk/core/server';
|
|
494
|
+
|
|
495
|
+
// Creer un compte vendeur
|
|
496
|
+
const { data: account } = await createConnectAccount({
|
|
497
|
+
type: 'express',
|
|
498
|
+
email: 'seller@example.com',
|
|
499
|
+
capabilities: {
|
|
500
|
+
cardPayments: { requested: true },
|
|
501
|
+
transfers: { requested: true },
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
// Lien d'onboarding
|
|
506
|
+
const { data: link } = await createAccountLink({
|
|
507
|
+
accountId: account.id,
|
|
508
|
+
refreshUrl: 'https://monsite.com/onboarding/refresh',
|
|
509
|
+
returnUrl: 'https://monsite.com/onboarding/complete',
|
|
510
|
+
type: 'account_onboarding',
|
|
511
|
+
});
|
|
512
|
+
// Rediriger le vendeur vers link.url
|
|
513
|
+
|
|
514
|
+
// Transferer des fonds
|
|
515
|
+
await createTransfer({
|
|
516
|
+
amount: 8000, // 80 EUR au vendeur
|
|
517
|
+
currency: 'eur',
|
|
518
|
+
destinationAccountId: account.id,
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
// Voir le solde
|
|
522
|
+
const { data: balance } = await getBalance();
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
## Webhooks
|
|
528
|
+
|
|
529
|
+
### Next.js App Router
|
|
530
|
+
|
|
531
|
+
```ts
|
|
532
|
+
// app/api/webhooks/stripe/route.ts
|
|
533
|
+
import '@/lib/stripe';
|
|
534
|
+
import { createNextWebhookHandler } from '@stripe-sdk/core/webhooks';
|
|
535
|
+
|
|
536
|
+
export const POST = createNextWebhookHandler({
|
|
537
|
+
handlers: {
|
|
538
|
+
'payment_intent.succeeded': async (event) => {
|
|
539
|
+
const pi = event.data.object;
|
|
540
|
+
console.log('Payment succeeded:', pi.id);
|
|
541
|
+
// Mettre a jour votre base de donnees
|
|
542
|
+
},
|
|
543
|
+
|
|
544
|
+
'customer.subscription.created': async (event) => {
|
|
545
|
+
const sub = event.data.object;
|
|
546
|
+
console.log('New subscription:', sub.id);
|
|
547
|
+
},
|
|
548
|
+
|
|
549
|
+
'customer.subscription.deleted': async (event) => {
|
|
550
|
+
const sub = event.data.object;
|
|
551
|
+
console.log('Subscription cancelled:', sub.id);
|
|
552
|
+
},
|
|
553
|
+
|
|
554
|
+
'invoice.payment_failed': async (event) => {
|
|
555
|
+
const invoice = event.data.object;
|
|
556
|
+
console.log('Payment failed for:', invoice.customer);
|
|
557
|
+
// Envoyer un email au client
|
|
558
|
+
},
|
|
559
|
+
|
|
560
|
+
'checkout.session.completed': async (event) => {
|
|
561
|
+
const session = event.data.object;
|
|
562
|
+
console.log('Checkout completed:', session.id);
|
|
563
|
+
},
|
|
564
|
+
},
|
|
565
|
+
onError: (error, event) => {
|
|
566
|
+
console.error('Webhook error:', error.message, event?.type);
|
|
567
|
+
},
|
|
568
|
+
});
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
### Next.js Pages Router
|
|
572
|
+
|
|
573
|
+
```ts
|
|
574
|
+
// pages/api/webhooks/stripe.ts
|
|
575
|
+
import '@/lib/stripe';
|
|
576
|
+
import { createPagesWebhookHandler } from '@stripe-sdk/core/webhooks';
|
|
577
|
+
|
|
578
|
+
export const config = { api: { bodyParser: false } };
|
|
579
|
+
|
|
580
|
+
export default createPagesWebhookHandler({
|
|
581
|
+
handlers: {
|
|
582
|
+
'payment_intent.succeeded': async (event) => { /* ... */ },
|
|
583
|
+
},
|
|
584
|
+
});
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
---
|
|
588
|
+
|
|
589
|
+
## Next.js - Routes API pre-construites
|
|
590
|
+
|
|
591
|
+
```ts
|
|
592
|
+
// app/api/create-payment-intent/route.ts
|
|
593
|
+
import '@/lib/stripe';
|
|
594
|
+
import { createPaymentIntentRoute } from '@stripe-sdk/core/next';
|
|
595
|
+
|
|
596
|
+
export const POST = createPaymentIntentRoute({
|
|
597
|
+
// Optionnel : modifier l'input avant creation
|
|
598
|
+
beforeCreate: async (input, request) => {
|
|
599
|
+
// Verifier l'authentification, forcer un montant, etc.
|
|
600
|
+
return { ...input, currency: 'eur' };
|
|
601
|
+
},
|
|
602
|
+
});
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
```ts
|
|
606
|
+
// app/api/create-checkout-session/route.ts
|
|
607
|
+
import '@/lib/stripe';
|
|
608
|
+
import { createCheckoutSessionRoute } from '@stripe-sdk/core/next';
|
|
609
|
+
|
|
610
|
+
export const POST = createCheckoutSessionRoute();
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
```ts
|
|
614
|
+
// app/api/create-portal-session/route.ts
|
|
615
|
+
import '@/lib/stripe';
|
|
616
|
+
import { createPortalSessionRoute } from '@stripe-sdk/core/next';
|
|
617
|
+
|
|
618
|
+
export const POST = createPortalSessionRoute();
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
---
|
|
622
|
+
|
|
623
|
+
## Gestion des erreurs
|
|
624
|
+
|
|
625
|
+
Toutes les fonctions serveur retournent un `SDKResult<T>` :
|
|
626
|
+
|
|
627
|
+
```ts
|
|
628
|
+
type SDKResult<T> =
|
|
629
|
+
| { data: T; error: null } // Succes
|
|
630
|
+
| { data: null; error: SDKError } // Erreur
|
|
631
|
+
|
|
632
|
+
type SDKError = {
|
|
633
|
+
message: string;
|
|
634
|
+
type: string;
|
|
635
|
+
code?: string;
|
|
636
|
+
statusCode?: number;
|
|
637
|
+
};
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
```ts
|
|
641
|
+
const result = await createPaymentIntent({ amount: 2000, currency: 'eur' });
|
|
642
|
+
|
|
643
|
+
if (result.error) {
|
|
644
|
+
console.error(result.error.message);
|
|
645
|
+
// "Your card was declined"
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
console.log(result.data.id); // pi_xxx - TypeScript infere le type correct
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
---
|
|
653
|
+
|
|
654
|
+
## Architecture
|
|
655
|
+
|
|
656
|
+
```
|
|
657
|
+
src/
|
|
658
|
+
server/ # Code serveur uniquement
|
|
659
|
+
stripe-client.ts # Init & singleton Stripe
|
|
660
|
+
payments/ # PaymentIntent, Checkout, PaymentLink, SetupIntent
|
|
661
|
+
customers/ # CRUD clients + Portail
|
|
662
|
+
subscriptions/ # CRUD abonnements
|
|
663
|
+
products/ # Produits & Prix
|
|
664
|
+
invoices/ # Facturation
|
|
665
|
+
refunds/ # Remboursements & Litiges
|
|
666
|
+
connect/ # Marketplace, Transfers, Payouts, Balance
|
|
667
|
+
coupons/ # Coupons & Codes promo
|
|
668
|
+
webhooks/ # Handler avec verification de signature
|
|
669
|
+
client/ # Code client (React)
|
|
670
|
+
providers/ # StripeProvider, StripeElementsProvider
|
|
671
|
+
hooks/ # usePayment, useSetupIntent, useCheckout
|
|
672
|
+
components/ # CheckoutForm, SetupForm, PricingTable, SubscriptionManager
|
|
673
|
+
next/ # Utilitaires specifiques Next.js
|
|
674
|
+
index.ts # Server Actions, API route helpers
|
|
675
|
+
types/ # Types TypeScript complets
|
|
676
|
+
utils/ # Gestion d'erreurs
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
## Imports
|
|
680
|
+
|
|
681
|
+
```ts
|
|
682
|
+
// Serveur uniquement
|
|
683
|
+
import { ... } from '@stripe-sdk/core/server';
|
|
684
|
+
|
|
685
|
+
// Client uniquement (React)
|
|
686
|
+
import { ... } from '@stripe-sdk/core/client';
|
|
687
|
+
|
|
688
|
+
// Webhooks
|
|
689
|
+
import { ... } from '@stripe-sdk/core/webhooks';
|
|
690
|
+
|
|
691
|
+
// Helpers Next.js
|
|
692
|
+
import { ... } from '@stripe-sdk/core/next';
|
|
693
|
+
|
|
694
|
+
// Tout (attention au tree-shaking)
|
|
695
|
+
import { ... } from '@stripe-sdk/core';
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
## Licence
|
|
699
|
+
|
|
700
|
+
MIT
|