@86d-app/payments 0.0.3
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/AGENTS.md +47 -0
- package/README.md +261 -0
- package/package.json +49 -0
- package/src/__tests__/service-impl.test.ts +589 -0
- package/src/admin/components/index.tsx +1 -0
- package/src/admin/components/payments-admin.mdx +27 -0
- package/src/admin/components/payments-admin.tsx +521 -0
- package/src/admin/endpoints/create-refund.ts +25 -0
- package/src/admin/endpoints/get-intent.ts +16 -0
- package/src/admin/endpoints/index.ts +11 -0
- package/src/admin/endpoints/list-intents.ts +36 -0
- package/src/admin/endpoints/list-refunds.ts +15 -0
- package/src/index.ts +58 -0
- package/src/mdx.d.ts +5 -0
- package/src/schema.ts +97 -0
- package/src/service-impl.ts +331 -0
- package/src/service.ts +160 -0
- package/src/store/endpoints/cancel-intent.ts +16 -0
- package/src/store/endpoints/confirm-intent.ts +16 -0
- package/src/store/endpoints/create-intent.ts +29 -0
- package/src/store/endpoints/delete-method.ts +17 -0
- package/src/store/endpoints/get-intent.ts +16 -0
- package/src/store/endpoints/index.ts +15 -0
- package/src/store/endpoints/list-methods.ts +17 -0
- package/tsconfig.json +9 -0
- package/vitest.config.ts +7 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# payments module
|
|
2
|
+
|
|
3
|
+
Provider-agnostic payment processing module. Tracks payment intents, saved payment methods, and refunds locally. Delegates actual payment processing to a configurable `PaymentProvider` (e.g. Stripe).
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
- `schema.ts` — `paymentIntent`, `paymentMethod`, `refund` entities
|
|
8
|
+
- `service.ts` — `PaymentController`, `PaymentProvider`, `PaymentIntent`, `PaymentMethod`, `Refund` types
|
|
9
|
+
- `service-impl.ts` — `createPaymentController(data, provider?)` factory
|
|
10
|
+
- `endpoints/store/` — customer-facing endpoints (create/confirm/cancel intent, manage payment methods)
|
|
11
|
+
- `endpoints/admin/` — admin endpoints (list, get, refund)
|
|
12
|
+
|
|
13
|
+
## PaymentProvider Interface
|
|
14
|
+
|
|
15
|
+
Users plug in a provider by implementing `PaymentProvider`:
|
|
16
|
+
- `createIntent(params)` — returns `{ providerIntentId, status, providerMetadata? }`
|
|
17
|
+
- `confirmIntent(providerIntentId)` — returns updated status
|
|
18
|
+
- `cancelIntent(providerIntentId)` — returns cancelled status
|
|
19
|
+
- `createRefund(params)` — returns `{ providerRefundId, status, providerMetadata? }`
|
|
20
|
+
|
|
21
|
+
The module works without a provider (offline mode) — intents are stored with `pending` status and transitions are handled locally.
|
|
22
|
+
|
|
23
|
+
## Store Endpoints
|
|
24
|
+
|
|
25
|
+
| Method | Path | Description |
|
|
26
|
+
|--------|------|-------------|
|
|
27
|
+
| POST | `/payments/intents` | Create payment intent |
|
|
28
|
+
| GET | `/payments/intents/:id` | Get intent by ID |
|
|
29
|
+
| POST | `/payments/intents/:id/confirm` | Confirm payment |
|
|
30
|
+
| POST | `/payments/intents/:id/cancel` | Cancel payment |
|
|
31
|
+
| GET | `/payments/methods` | List customer's payment methods |
|
|
32
|
+
| DELETE | `/payments/methods/:id` | Delete a payment method |
|
|
33
|
+
|
|
34
|
+
## Admin Endpoints
|
|
35
|
+
|
|
36
|
+
| Method | Path | Description |
|
|
37
|
+
|--------|------|-------------|
|
|
38
|
+
| GET | `/admin/payments` | List all intents (filter: customerId, status, orderId) |
|
|
39
|
+
| GET | `/admin/payments/:id` | Get intent detail |
|
|
40
|
+
| POST | `/admin/payments/:id/refund` | Create refund |
|
|
41
|
+
| GET | `/admin/payments/:id/refunds` | List refunds for intent |
|
|
42
|
+
|
|
43
|
+
## Tests (34 tests)
|
|
44
|
+
|
|
45
|
+
Run: `bun test` from this directory.
|
|
46
|
+
|
|
47
|
+
Covers: createIntent, getIntent, confirmIntent, cancelIntent, listIntents, savePaymentMethod, getPaymentMethod, listPaymentMethods, deletePaymentMethod, createRefund, getRefund, listRefunds — all with and without provider.
|
package/README.md
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://86d.app">
|
|
3
|
+
<img src="https://86d.app/logo" height="96" alt="86d" />
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
Dynamic Commerce
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://vercel.com/changelog"><strong>npm</strong></a> ·
|
|
13
|
+
<a href="https://x.com/86d_app"><strong>X</strong></a> ·
|
|
14
|
+
<a href="https://vercel.com/templates"><strong>LinkedIn</strong></a>
|
|
15
|
+
</p>
|
|
16
|
+
<br/>
|
|
17
|
+
|
|
18
|
+
> [!WARNING]
|
|
19
|
+
> This project is under active development and is not ready for production use. Please proceed with caution. Use at your own risk.
|
|
20
|
+
|
|
21
|
+
# @86d-app/payments
|
|
22
|
+
|
|
23
|
+
Provider-agnostic payment processing for the 86d commerce platform. Tracks payment intents, saved payment methods, and refunds locally. Delegates actual processing to a configurable `PaymentProvider` (Stripe, Square, PayPal, etc.).
|
|
24
|
+
|
|
25
|
+
 
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
npm install @86d-app/payments
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import payments from "@86d-app/payments";
|
|
37
|
+
import { createModuleClient } from "@86d-app/core";
|
|
38
|
+
|
|
39
|
+
// Without a provider (offline/test mode)
|
|
40
|
+
const client = createModuleClient([payments()]);
|
|
41
|
+
|
|
42
|
+
// With a Stripe provider
|
|
43
|
+
import { StripePaymentProvider } from "@86d-app/stripe";
|
|
44
|
+
const provider = new StripePaymentProvider("sk_live_...");
|
|
45
|
+
const client = createModuleClient([payments({ provider, currency: "USD" })]);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Configuration
|
|
49
|
+
|
|
50
|
+
| Option | Type | Default | Description |
|
|
51
|
+
|---|---|---|---|
|
|
52
|
+
| `currency` | `string` | `"USD"` | Default currency for payment intents |
|
|
53
|
+
| `provider` | `PaymentProvider` | `undefined` | Payment processor implementation |
|
|
54
|
+
|
|
55
|
+
## PaymentProvider Interface
|
|
56
|
+
|
|
57
|
+
Implement this interface to connect any payment processor:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
interface PaymentProvider {
|
|
61
|
+
createIntent(params: {
|
|
62
|
+
amount: number; // in smallest currency unit (e.g. cents)
|
|
63
|
+
currency: string;
|
|
64
|
+
metadata?: Record<string, unknown>;
|
|
65
|
+
}): Promise<ProviderIntentResult>;
|
|
66
|
+
|
|
67
|
+
confirmIntent(providerIntentId: string): Promise<ProviderIntentResult>;
|
|
68
|
+
|
|
69
|
+
cancelIntent(providerIntentId: string): Promise<ProviderIntentResult>;
|
|
70
|
+
|
|
71
|
+
createRefund(params: {
|
|
72
|
+
providerIntentId: string;
|
|
73
|
+
amount?: number; // partial refund; omit for full refund
|
|
74
|
+
reason?: string;
|
|
75
|
+
}): Promise<ProviderRefundResult>;
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Offline mode:** If no provider is configured, intents are stored locally with `pending` status and status transitions are handled in-memory. Useful for testing and development.
|
|
80
|
+
|
|
81
|
+
## Store Endpoints
|
|
82
|
+
|
|
83
|
+
| Method | Path | Description |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| `POST` | `/payments/intents` | Create a payment intent |
|
|
86
|
+
| `GET` | `/payments/intents/:id` | Get intent by ID |
|
|
87
|
+
| `POST` | `/payments/intents/:id/confirm` | Confirm payment |
|
|
88
|
+
| `POST` | `/payments/intents/:id/cancel` | Cancel payment |
|
|
89
|
+
| `GET` | `/payments/methods` | List customer's saved payment methods |
|
|
90
|
+
| `DELETE` | `/payments/methods/:id` | Delete a payment method |
|
|
91
|
+
|
|
92
|
+
## Admin Endpoints
|
|
93
|
+
|
|
94
|
+
| Method | Path | Description |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| `GET` | `/admin/payments` | List all intents (filter: `customerId`, `status`, `orderId`) |
|
|
97
|
+
| `GET` | `/admin/payments/:id` | Get intent detail |
|
|
98
|
+
| `POST` | `/admin/payments/:id/refund` | Issue a refund |
|
|
99
|
+
| `GET` | `/admin/payments/:id/refunds` | List refunds for an intent |
|
|
100
|
+
|
|
101
|
+
## Controller API
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
// ── Payment intents ─────────────────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
controller.createIntent(params: {
|
|
107
|
+
amount: number; // in smallest currency unit (e.g. cents)
|
|
108
|
+
currency?: string; // default: module currency option
|
|
109
|
+
customerId?: string;
|
|
110
|
+
email?: string;
|
|
111
|
+
orderId?: string;
|
|
112
|
+
checkoutSessionId?: string;
|
|
113
|
+
metadata?: Record<string, unknown>;
|
|
114
|
+
}): Promise<PaymentIntent>
|
|
115
|
+
|
|
116
|
+
controller.getIntent(id: string): Promise<PaymentIntent | null>
|
|
117
|
+
|
|
118
|
+
controller.confirmIntent(id: string): Promise<PaymentIntent | null>
|
|
119
|
+
|
|
120
|
+
controller.cancelIntent(id: string): Promise<PaymentIntent | null>
|
|
121
|
+
|
|
122
|
+
controller.listIntents(params?: {
|
|
123
|
+
customerId?: string;
|
|
124
|
+
status?: PaymentIntentStatus;
|
|
125
|
+
orderId?: string;
|
|
126
|
+
take?: number;
|
|
127
|
+
skip?: number;
|
|
128
|
+
}): Promise<PaymentIntent[]>
|
|
129
|
+
|
|
130
|
+
// ── Payment methods ─────────────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
// Saves a payment method; if isDefault=true, clears all other defaults
|
|
133
|
+
controller.savePaymentMethod(params: {
|
|
134
|
+
customerId: string;
|
|
135
|
+
providerMethodId: string; // e.g. Stripe's pm_xxx
|
|
136
|
+
type?: string; // "card" | "bank_transfer" | "wallet"
|
|
137
|
+
last4?: string;
|
|
138
|
+
brand?: string; // "visa" | "mastercard" | etc.
|
|
139
|
+
expiryMonth?: number;
|
|
140
|
+
expiryYear?: number;
|
|
141
|
+
isDefault?: boolean;
|
|
142
|
+
}): Promise<PaymentMethod>
|
|
143
|
+
|
|
144
|
+
controller.getPaymentMethod(id: string): Promise<PaymentMethod | null>
|
|
145
|
+
|
|
146
|
+
controller.listPaymentMethods(customerId: string): Promise<PaymentMethod[]>
|
|
147
|
+
|
|
148
|
+
controller.deletePaymentMethod(id: string): Promise<boolean>
|
|
149
|
+
|
|
150
|
+
// ── Refunds ─────────────────────────────────────────────────────────────────
|
|
151
|
+
|
|
152
|
+
// Throws if payment intent not found; marks intent status as "refunded"
|
|
153
|
+
controller.createRefund(params: {
|
|
154
|
+
intentId: string;
|
|
155
|
+
amount?: number; // omit for full refund
|
|
156
|
+
reason?: string;
|
|
157
|
+
}): Promise<Refund>
|
|
158
|
+
|
|
159
|
+
controller.getRefund(id: string): Promise<Refund | null>
|
|
160
|
+
|
|
161
|
+
controller.listRefunds(intentId: string): Promise<Refund[]>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Example: Checkout Payment Flow
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
// 1. Customer initiates checkout — create intent
|
|
168
|
+
const intent = await controller.createIntent({
|
|
169
|
+
amount: 4999, // $49.99
|
|
170
|
+
currency: "USD",
|
|
171
|
+
customerId: "cust_123",
|
|
172
|
+
orderId: "ord_456",
|
|
173
|
+
});
|
|
174
|
+
// intent.status === "pending"
|
|
175
|
+
// With Stripe: intent.providerMetadata.clientSecret → send to frontend
|
|
176
|
+
|
|
177
|
+
// 2. Customer completes payment on frontend → call confirm
|
|
178
|
+
const confirmed = await controller.confirmIntent(intent.id);
|
|
179
|
+
// confirmed.status === "succeeded"
|
|
180
|
+
|
|
181
|
+
// 3. Customer requests refund
|
|
182
|
+
const refund = await controller.createRefund({
|
|
183
|
+
intentId: intent.id,
|
|
184
|
+
reason: "customer request",
|
|
185
|
+
});
|
|
186
|
+
// refund.status === "succeeded"
|
|
187
|
+
// intent.status is now "refunded"
|
|
188
|
+
|
|
189
|
+
// 4. Save a payment method for future use
|
|
190
|
+
const method = await controller.savePaymentMethod({
|
|
191
|
+
customerId: "cust_123",
|
|
192
|
+
providerMethodId: "pm_stripe_xxx",
|
|
193
|
+
type: "card",
|
|
194
|
+
last4: "4242",
|
|
195
|
+
brand: "visa",
|
|
196
|
+
isDefault: true,
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Payment Intent Statuses
|
|
201
|
+
|
|
202
|
+
| Status | Description |
|
|
203
|
+
|---|---|
|
|
204
|
+
| `pending` | Intent created, payment not yet initiated |
|
|
205
|
+
| `processing` | Payment is being processed |
|
|
206
|
+
| `succeeded` | Payment completed successfully |
|
|
207
|
+
| `failed` | Payment failed |
|
|
208
|
+
| `cancelled` | Intent was cancelled |
|
|
209
|
+
| `refunded` | Payment has been refunded |
|
|
210
|
+
|
|
211
|
+
## Types
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
type PaymentIntentStatus =
|
|
215
|
+
| "pending" | "processing" | "succeeded"
|
|
216
|
+
| "failed" | "cancelled" | "refunded";
|
|
217
|
+
|
|
218
|
+
type RefundStatus = "pending" | "succeeded" | "failed";
|
|
219
|
+
|
|
220
|
+
interface PaymentIntent {
|
|
221
|
+
id: string;
|
|
222
|
+
providerIntentId?: string; // e.g. Stripe's pi_xxx
|
|
223
|
+
customerId?: string;
|
|
224
|
+
email?: string;
|
|
225
|
+
amount: number;
|
|
226
|
+
currency: string;
|
|
227
|
+
status: PaymentIntentStatus;
|
|
228
|
+
paymentMethodId?: string;
|
|
229
|
+
orderId?: string;
|
|
230
|
+
checkoutSessionId?: string;
|
|
231
|
+
metadata: Record<string, unknown>;
|
|
232
|
+
providerMetadata: Record<string, unknown>;
|
|
233
|
+
createdAt: Date;
|
|
234
|
+
updatedAt: Date;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
interface PaymentMethod {
|
|
238
|
+
id: string;
|
|
239
|
+
customerId: string;
|
|
240
|
+
providerMethodId: string;
|
|
241
|
+
type: string;
|
|
242
|
+
last4?: string;
|
|
243
|
+
brand?: string;
|
|
244
|
+
expiryMonth?: number;
|
|
245
|
+
expiryYear?: number;
|
|
246
|
+
isDefault: boolean;
|
|
247
|
+
createdAt: Date;
|
|
248
|
+
updatedAt: Date;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
interface Refund {
|
|
252
|
+
id: string;
|
|
253
|
+
paymentIntentId: string;
|
|
254
|
+
providerRefundId: string;
|
|
255
|
+
amount: number;
|
|
256
|
+
reason?: string;
|
|
257
|
+
status: RefundStatus;
|
|
258
|
+
createdAt: Date;
|
|
259
|
+
updatedAt: Date;
|
|
260
|
+
}
|
|
261
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@86d-app/payments",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "Payment processing abstraction for 86d commerce platform",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"commerce",
|
|
7
|
+
"payments",
|
|
8
|
+
"payment-intents",
|
|
9
|
+
"refunds",
|
|
10
|
+
"86d"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "86d <chat@86d.app>",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/86d-app/86d.git",
|
|
17
|
+
"directory": "modules/payments"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/86d-app/86d/tree/main/modules/payments",
|
|
20
|
+
"type": "module",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./src/index.ts",
|
|
24
|
+
"import": "./src/index.ts",
|
|
25
|
+
"default": "./src/index.ts"
|
|
26
|
+
},
|
|
27
|
+
"./admin-components": "./src/admin/components",
|
|
28
|
+
"./*": "./src/*"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc",
|
|
32
|
+
"check": "biome check src",
|
|
33
|
+
"check:fix": "biome check --write src",
|
|
34
|
+
"clean": "git clean -xdf .cache .turbo dist node_modules",
|
|
35
|
+
"dev": "tsc",
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"test:watch": "vitest watch",
|
|
38
|
+
"typecheck": "tsc --noEmit --emitDeclarationOnly false"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@86d-app/core": "workspace:*"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@biomejs/biome": "catalog:",
|
|
45
|
+
"@types/react": "catalog:react",
|
|
46
|
+
"typescript": "catalog:",
|
|
47
|
+
"vitest": "catalog:vite"
|
|
48
|
+
}
|
|
49
|
+
}
|