@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/src/schema.ts ADDED
@@ -0,0 +1,97 @@
1
+ import type { ModuleSchema } from "@86d-app/core";
2
+
3
+ export const paymentsSchema = {
4
+ paymentIntent: {
5
+ fields: {
6
+ id: { type: "string", required: true },
7
+ /** Provider-assigned intent ID (e.g. Stripe's pi_xxx) */
8
+ providerIntentId: { type: "string", required: false },
9
+ customerId: { type: "string", required: false },
10
+ email: { type: "string", required: false },
11
+ /** Amount in smallest currency unit (e.g. cents) */
12
+ amount: { type: "number", required: true },
13
+ currency: { type: "string", required: true, defaultValue: "USD" },
14
+ status: {
15
+ type: [
16
+ "pending",
17
+ "processing",
18
+ "succeeded",
19
+ "failed",
20
+ "cancelled",
21
+ "refunded",
22
+ ],
23
+ required: true,
24
+ defaultValue: "pending",
25
+ },
26
+ paymentMethodId: { type: "string", required: false },
27
+ orderId: { type: "string", required: false },
28
+ checkoutSessionId: { type: "string", required: false },
29
+ metadata: { type: "json", required: false, defaultValue: {} },
30
+ providerMetadata: { type: "json", required: false, defaultValue: {} },
31
+ createdAt: {
32
+ type: "date",
33
+ required: true,
34
+ defaultValue: () => new Date(),
35
+ },
36
+ updatedAt: {
37
+ type: "date",
38
+ required: true,
39
+ defaultValue: () => new Date(),
40
+ onUpdate: () => new Date(),
41
+ },
42
+ },
43
+ },
44
+ paymentMethod: {
45
+ fields: {
46
+ id: { type: "string", required: true },
47
+ customerId: { type: "string", required: true },
48
+ /** Provider-assigned method ID (e.g. Stripe's pm_xxx) */
49
+ providerMethodId: { type: "string", required: true },
50
+ /** card | bank_transfer | wallet */
51
+ type: { type: "string", required: true, defaultValue: "card" },
52
+ last4: { type: "string", required: false },
53
+ brand: { type: "string", required: false },
54
+ expiryMonth: { type: "number", required: false },
55
+ expiryYear: { type: "number", required: false },
56
+ isDefault: { type: "boolean", required: true, defaultValue: false },
57
+ createdAt: {
58
+ type: "date",
59
+ required: true,
60
+ defaultValue: () => new Date(),
61
+ },
62
+ updatedAt: {
63
+ type: "date",
64
+ required: true,
65
+ defaultValue: () => new Date(),
66
+ onUpdate: () => new Date(),
67
+ },
68
+ },
69
+ },
70
+ refund: {
71
+ fields: {
72
+ id: { type: "string", required: true },
73
+ paymentIntentId: { type: "string", required: true },
74
+ /** Provider-assigned refund ID */
75
+ providerRefundId: { type: "string", required: true },
76
+ /** Refund amount in smallest currency unit */
77
+ amount: { type: "number", required: true },
78
+ reason: { type: "string", required: false },
79
+ status: {
80
+ type: ["pending", "succeeded", "failed"],
81
+ required: true,
82
+ defaultValue: "pending",
83
+ },
84
+ createdAt: {
85
+ type: "date",
86
+ required: true,
87
+ defaultValue: () => new Date(),
88
+ },
89
+ updatedAt: {
90
+ type: "date",
91
+ required: true,
92
+ defaultValue: () => new Date(),
93
+ onUpdate: () => new Date(),
94
+ },
95
+ },
96
+ },
97
+ } satisfies ModuleSchema;
@@ -0,0 +1,331 @@
1
+ import type { ModuleDataService } from "@86d-app/core";
2
+ import type {
3
+ PaymentController,
4
+ PaymentIntent,
5
+ PaymentIntentStatus,
6
+ PaymentMethod,
7
+ PaymentProvider,
8
+ Refund,
9
+ } from "./service";
10
+
11
+ export function createPaymentController(
12
+ data: ModuleDataService,
13
+ provider?: PaymentProvider,
14
+ ): PaymentController {
15
+ return {
16
+ async createIntent(params) {
17
+ const id = crypto.randomUUID();
18
+ const now = new Date();
19
+
20
+ let providerIntentId: string | undefined;
21
+ let providerMetadata: Record<string, unknown> = {};
22
+ let status: PaymentIntentStatus = "pending";
23
+
24
+ if (provider) {
25
+ const result = await provider.createIntent({
26
+ amount: params.amount,
27
+ currency: params.currency ?? "USD",
28
+ metadata: params.metadata,
29
+ });
30
+ providerIntentId = result.providerIntentId;
31
+ providerMetadata = result.providerMetadata ?? {};
32
+ status = result.status;
33
+ }
34
+
35
+ const intent: PaymentIntent = {
36
+ id,
37
+ providerIntentId,
38
+ customerId: params.customerId,
39
+ email: params.email,
40
+ amount: params.amount,
41
+ currency: params.currency ?? "USD",
42
+ status,
43
+ orderId: params.orderId,
44
+ checkoutSessionId: params.checkoutSessionId,
45
+ metadata: params.metadata ?? {},
46
+ providerMetadata,
47
+ createdAt: now,
48
+ updatedAt: now,
49
+ };
50
+ // biome-ignore lint/suspicious/noExplicitAny: ModuleDataService requires any
51
+ await data.upsert("paymentIntent", id, intent as Record<string, any>);
52
+ return intent;
53
+ },
54
+
55
+ async getIntent(id) {
56
+ const raw = await data.get("paymentIntent", id);
57
+ if (!raw) return null;
58
+ return raw as unknown as PaymentIntent;
59
+ },
60
+
61
+ async confirmIntent(id) {
62
+ const existing = await data.get("paymentIntent", id);
63
+ if (!existing) return null;
64
+ const intent = existing as unknown as PaymentIntent;
65
+ if (intent.status === "succeeded") return intent;
66
+
67
+ let newStatus: PaymentIntentStatus = "succeeded";
68
+ let newProviderMetadata = intent.providerMetadata;
69
+
70
+ if (provider && intent.providerIntentId) {
71
+ const result = await provider.confirmIntent(intent.providerIntentId);
72
+ newStatus = result.status === "succeeded" ? "succeeded" : result.status;
73
+ newProviderMetadata = result.providerMetadata ?? newProviderMetadata;
74
+ }
75
+
76
+ const updated: PaymentIntent = {
77
+ ...intent,
78
+ status: newStatus,
79
+ providerMetadata: newProviderMetadata,
80
+ updatedAt: new Date(),
81
+ };
82
+ // biome-ignore lint/suspicious/noExplicitAny: ModuleDataService requires any
83
+ await data.upsert("paymentIntent", id, updated as Record<string, any>);
84
+ return updated;
85
+ },
86
+
87
+ async cancelIntent(id) {
88
+ const existing = await data.get("paymentIntent", id);
89
+ if (!existing) return null;
90
+ const intent = existing as unknown as PaymentIntent;
91
+ if (intent.status === "cancelled") return intent;
92
+
93
+ let newProviderMetadata = intent.providerMetadata;
94
+
95
+ if (provider && intent.providerIntentId) {
96
+ const result = await provider.cancelIntent(intent.providerIntentId);
97
+ newProviderMetadata = result.providerMetadata ?? newProviderMetadata;
98
+ }
99
+
100
+ const updated: PaymentIntent = {
101
+ ...intent,
102
+ status: "cancelled",
103
+ providerMetadata: newProviderMetadata,
104
+ updatedAt: new Date(),
105
+ };
106
+ // biome-ignore lint/suspicious/noExplicitAny: ModuleDataService requires any
107
+ await data.upsert("paymentIntent", id, updated as Record<string, any>);
108
+ return updated;
109
+ },
110
+
111
+ async listIntents(params) {
112
+ // Push available filters to where clause
113
+ // biome-ignore lint/suspicious/noExplicitAny: JSONB where filter
114
+ const where: Record<string, any> = {};
115
+ if (params?.customerId) where.customerId = params.customerId;
116
+ if (params?.status) where.status = params.status;
117
+ if (params?.orderId) where.orderId = params.orderId;
118
+
119
+ const all = await data.findMany("paymentIntent", {
120
+ ...(Object.keys(where).length > 0 ? { where } : {}),
121
+ ...(params?.take !== undefined ? { take: params.take } : {}),
122
+ ...(params?.skip !== undefined ? { skip: params.skip } : {}),
123
+ });
124
+ return all as unknown as PaymentIntent[];
125
+ },
126
+
127
+ async savePaymentMethod(params) {
128
+ const id = crypto.randomUUID();
129
+ const now = new Date();
130
+
131
+ // If isDefault is set, clear other defaults for this customer
132
+ if (params.isDefault) {
133
+ const existing = await data.findMany("paymentMethod", {
134
+ where: { customerId: params.customerId, isDefault: true },
135
+ });
136
+ const methods = existing as unknown as PaymentMethod[];
137
+ for (const m of methods) {
138
+ const cleared: PaymentMethod = {
139
+ ...m,
140
+ isDefault: false,
141
+ updatedAt: now,
142
+ };
143
+ await data.upsert(
144
+ "paymentMethod",
145
+ m.id,
146
+ // biome-ignore lint/suspicious/noExplicitAny: ModuleDataService requires any
147
+ cleared as unknown as Record<string, any>,
148
+ );
149
+ }
150
+ }
151
+
152
+ const method: PaymentMethod = {
153
+ id,
154
+ customerId: params.customerId,
155
+ providerMethodId: params.providerMethodId,
156
+ type: params.type ?? "card",
157
+ last4: params.last4,
158
+ brand: params.brand,
159
+ expiryMonth: params.expiryMonth,
160
+ expiryYear: params.expiryYear,
161
+ isDefault: params.isDefault ?? false,
162
+ createdAt: now,
163
+ updatedAt: now,
164
+ };
165
+ // biome-ignore lint/suspicious/noExplicitAny: ModuleDataService requires any
166
+ await data.upsert("paymentMethod", id, method as Record<string, any>);
167
+ return method;
168
+ },
169
+
170
+ async getPaymentMethod(id) {
171
+ const raw = await data.get("paymentMethod", id);
172
+ if (!raw) return null;
173
+ return raw as unknown as PaymentMethod;
174
+ },
175
+
176
+ async listPaymentMethods(customerId) {
177
+ const all = await data.findMany("paymentMethod", {
178
+ where: { customerId },
179
+ });
180
+ return all as unknown as PaymentMethod[];
181
+ },
182
+
183
+ async deletePaymentMethod(id) {
184
+ const existing = await data.get("paymentMethod", id);
185
+ if (!existing) return false;
186
+ await data.delete("paymentMethod", id);
187
+ return true;
188
+ },
189
+
190
+ async createRefund(params) {
191
+ const intent = await data.get("paymentIntent", params.intentId);
192
+ if (!intent) throw new Error("Payment intent not found");
193
+ const pi = intent as unknown as PaymentIntent;
194
+
195
+ const refundAmount = params.amount ?? pi.amount;
196
+
197
+ let providerRefundId: string;
198
+ let refundStatus: Refund["status"] = "succeeded";
199
+
200
+ if (provider && pi.providerIntentId) {
201
+ const result = await provider.createRefund({
202
+ providerIntentId: pi.providerIntentId,
203
+ amount: params.amount,
204
+ reason: params.reason,
205
+ });
206
+ providerRefundId = result.providerRefundId;
207
+ refundStatus = result.status;
208
+ } else if (pi.providerIntentId && !provider) {
209
+ throw new Error(
210
+ "Cannot refund: payment was created through a provider but no provider is configured",
211
+ );
212
+ } else {
213
+ // Local-only intent (no external provider was used)
214
+ providerRefundId = `local_re_${crypto.randomUUID()}`;
215
+ }
216
+
217
+ const id = crypto.randomUUID();
218
+ const now = new Date();
219
+ const refund: Refund = {
220
+ id,
221
+ paymentIntentId: params.intentId,
222
+ providerRefundId,
223
+ amount: refundAmount,
224
+ reason: params.reason,
225
+ status: refundStatus,
226
+ createdAt: now,
227
+ updatedAt: now,
228
+ };
229
+ // biome-ignore lint/suspicious/noExplicitAny: ModuleDataService requires any
230
+ await data.upsert("refund", id, refund as Record<string, any>);
231
+
232
+ // Mark intent as refunded
233
+ const updatedIntent: PaymentIntent = {
234
+ ...pi,
235
+ status: "refunded",
236
+ updatedAt: now,
237
+ };
238
+ await data.upsert(
239
+ "paymentIntent",
240
+ params.intentId,
241
+ // biome-ignore lint/suspicious/noExplicitAny: ModuleDataService requires any
242
+ updatedIntent as unknown as Record<string, any>,
243
+ );
244
+
245
+ return refund;
246
+ },
247
+
248
+ async getRefund(id) {
249
+ const raw = await data.get("refund", id);
250
+ if (!raw) return null;
251
+ return raw as unknown as Refund;
252
+ },
253
+
254
+ async listRefunds(intentId) {
255
+ const all = await data.findMany("refund", {
256
+ where: { paymentIntentId: intentId },
257
+ });
258
+ return all as unknown as Refund[];
259
+ },
260
+
261
+ async handleWebhookEvent(params) {
262
+ const all = await data.findMany("paymentIntent", {
263
+ where: { providerIntentId: params.providerIntentId },
264
+ take: 1,
265
+ });
266
+ const intents = all as unknown as PaymentIntent[];
267
+ if (intents.length === 0) return null;
268
+
269
+ const intent = intents[0];
270
+ if (intent.status === params.status) return intent;
271
+
272
+ const updated: PaymentIntent = {
273
+ ...intent,
274
+ status: params.status,
275
+ providerMetadata: {
276
+ ...intent.providerMetadata,
277
+ ...(params.providerMetadata ?? {}),
278
+ },
279
+ updatedAt: new Date(),
280
+ };
281
+ await data.upsert(
282
+ "paymentIntent",
283
+ intent.id,
284
+ // biome-ignore lint/suspicious/noExplicitAny: ModuleDataService requires any
285
+ updated as Record<string, any>,
286
+ );
287
+ return updated;
288
+ },
289
+
290
+ async handleWebhookRefund(params) {
291
+ const all = await data.findMany("paymentIntent", {
292
+ where: { providerIntentId: params.providerIntentId },
293
+ take: 1,
294
+ });
295
+ const intents = all as unknown as PaymentIntent[];
296
+ if (intents.length === 0) return null;
297
+
298
+ const intent = intents[0];
299
+ const now = new Date();
300
+ const refundAmount = params.amount ?? intent.amount;
301
+
302
+ const refundId = crypto.randomUUID();
303
+ const refund: Refund = {
304
+ id: refundId,
305
+ paymentIntentId: intent.id,
306
+ providerRefundId: params.providerRefundId,
307
+ amount: refundAmount,
308
+ reason: params.reason,
309
+ status: "succeeded",
310
+ createdAt: now,
311
+ updatedAt: now,
312
+ };
313
+ // biome-ignore lint/suspicious/noExplicitAny: ModuleDataService requires any
314
+ await data.upsert("refund", refundId, refund as Record<string, any>);
315
+
316
+ const updatedIntent: PaymentIntent = {
317
+ ...intent,
318
+ status: "refunded",
319
+ updatedAt: now,
320
+ };
321
+ await data.upsert(
322
+ "paymentIntent",
323
+ intent.id,
324
+ // biome-ignore lint/suspicious/noExplicitAny: ModuleDataService requires any
325
+ updatedIntent as unknown as Record<string, any>,
326
+ );
327
+
328
+ return { intent: updatedIntent, refund };
329
+ },
330
+ };
331
+ }
package/src/service.ts ADDED
@@ -0,0 +1,160 @@
1
+ import type { ModuleController } from "@86d-app/core";
2
+
3
+ export type PaymentIntentStatus =
4
+ | "pending"
5
+ | "processing"
6
+ | "succeeded"
7
+ | "failed"
8
+ | "cancelled"
9
+ | "refunded";
10
+
11
+ export type RefundStatus = "pending" | "succeeded" | "failed";
12
+
13
+ export interface PaymentIntent {
14
+ id: string;
15
+ providerIntentId?: string | undefined;
16
+ customerId?: string | undefined;
17
+ email?: string | undefined;
18
+ amount: number;
19
+ currency: string;
20
+ status: PaymentIntentStatus;
21
+ paymentMethodId?: string | undefined;
22
+ orderId?: string | undefined;
23
+ checkoutSessionId?: string | undefined;
24
+ metadata: Record<string, unknown>;
25
+ providerMetadata: Record<string, unknown>;
26
+ createdAt: Date;
27
+ updatedAt: Date;
28
+ }
29
+
30
+ export interface PaymentMethod {
31
+ id: string;
32
+ customerId: string;
33
+ providerMethodId: string;
34
+ type: string;
35
+ last4?: string | undefined;
36
+ brand?: string | undefined;
37
+ expiryMonth?: number | undefined;
38
+ expiryYear?: number | undefined;
39
+ isDefault: boolean;
40
+ createdAt: Date;
41
+ updatedAt: Date;
42
+ }
43
+
44
+ export interface Refund {
45
+ id: string;
46
+ paymentIntentId: string;
47
+ providerRefundId: string;
48
+ amount: number;
49
+ reason?: string | undefined;
50
+ status: RefundStatus;
51
+ createdAt: Date;
52
+ updatedAt: Date;
53
+ }
54
+
55
+ // ── Provider interface ────────────────────────────────────────────────────────
56
+
57
+ export interface ProviderIntentResult {
58
+ providerIntentId: string;
59
+ status: "pending" | "processing" | "succeeded" | "failed" | "cancelled";
60
+ providerMetadata?: Record<string, unknown> | undefined;
61
+ }
62
+
63
+ export interface ProviderRefundResult {
64
+ providerRefundId: string;
65
+ status: "pending" | "succeeded" | "failed";
66
+ providerMetadata?: Record<string, unknown> | undefined;
67
+ }
68
+
69
+ /** Implement this interface to connect a payment processor (e.g. Stripe). */
70
+ export interface PaymentProvider {
71
+ createIntent(params: {
72
+ amount: number;
73
+ currency: string;
74
+ metadata?: Record<string, unknown> | undefined;
75
+ }): Promise<ProviderIntentResult>;
76
+
77
+ confirmIntent(providerIntentId: string): Promise<ProviderIntentResult>;
78
+
79
+ cancelIntent(providerIntentId: string): Promise<ProviderIntentResult>;
80
+
81
+ createRefund(params: {
82
+ providerIntentId: string;
83
+ amount?: number | undefined;
84
+ reason?: string | undefined;
85
+ }): Promise<ProviderRefundResult>;
86
+ }
87
+
88
+ // ── Controller interface ──────────────────────────────────────────────────────
89
+
90
+ export interface PaymentController extends ModuleController {
91
+ // ── Intents ───────────────────────────────────────────────────────────
92
+ createIntent(params: {
93
+ amount: number;
94
+ currency?: string | undefined;
95
+ customerId?: string | undefined;
96
+ email?: string | undefined;
97
+ orderId?: string | undefined;
98
+ checkoutSessionId?: string | undefined;
99
+ metadata?: Record<string, unknown> | undefined;
100
+ }): Promise<PaymentIntent>;
101
+
102
+ getIntent(id: string): Promise<PaymentIntent | null>;
103
+
104
+ confirmIntent(id: string): Promise<PaymentIntent | null>;
105
+
106
+ cancelIntent(id: string): Promise<PaymentIntent | null>;
107
+
108
+ listIntents(params?: {
109
+ customerId?: string | undefined;
110
+ status?: PaymentIntentStatus | undefined;
111
+ orderId?: string | undefined;
112
+ take?: number | undefined;
113
+ skip?: number | undefined;
114
+ }): Promise<PaymentIntent[]>;
115
+
116
+ // ── Payment methods ───────────────────────────────────────────────────
117
+ savePaymentMethod(params: {
118
+ customerId: string;
119
+ providerMethodId: string;
120
+ type?: string | undefined;
121
+ last4?: string | undefined;
122
+ brand?: string | undefined;
123
+ expiryMonth?: number | undefined;
124
+ expiryYear?: number | undefined;
125
+ isDefault?: boolean | undefined;
126
+ }): Promise<PaymentMethod>;
127
+
128
+ getPaymentMethod(id: string): Promise<PaymentMethod | null>;
129
+
130
+ listPaymentMethods(customerId: string): Promise<PaymentMethod[]>;
131
+
132
+ deletePaymentMethod(id: string): Promise<boolean>;
133
+
134
+ // ── Refunds ───────────────────────────────────────────────────────────
135
+ createRefund(params: {
136
+ intentId: string;
137
+ amount?: number | undefined;
138
+ reason?: string | undefined;
139
+ }): Promise<Refund>;
140
+
141
+ getRefund(id: string): Promise<Refund | null>;
142
+
143
+ listRefunds(intentId: string): Promise<Refund[]>;
144
+
145
+ // ── Webhook handling ──────────────────────────────────────────────────
146
+ /** Look up a payment intent by its provider-assigned ID and update its status. */
147
+ handleWebhookEvent(params: {
148
+ providerIntentId: string;
149
+ status: PaymentIntentStatus;
150
+ providerMetadata?: Record<string, unknown> | undefined;
151
+ }): Promise<PaymentIntent | null>;
152
+
153
+ /** Look up a payment intent by its provider-assigned ID and record a refund. */
154
+ handleWebhookRefund(params: {
155
+ providerIntentId: string;
156
+ providerRefundId: string;
157
+ amount?: number | undefined;
158
+ reason?: string | undefined;
159
+ }): Promise<{ intent: PaymentIntent; refund: Refund } | null>;
160
+ }
@@ -0,0 +1,16 @@
1
+ import { createStoreEndpoint, z } from "@86d-app/core";
2
+ import type { PaymentController } from "../../service";
3
+
4
+ export const cancelIntent = createStoreEndpoint(
5
+ "/payments/intents/:id/cancel",
6
+ {
7
+ method: "POST",
8
+ params: z.object({ id: z.string() }),
9
+ },
10
+ async (ctx) => {
11
+ const controller = ctx.context.controllers.payments as PaymentController;
12
+ const intent = await controller.cancelIntent(ctx.params.id);
13
+ if (!intent) return { error: "Payment intent not found", status: 404 };
14
+ return { intent };
15
+ },
16
+ );
@@ -0,0 +1,16 @@
1
+ import { createStoreEndpoint, z } from "@86d-app/core";
2
+ import type { PaymentController } from "../../service";
3
+
4
+ export const confirmIntent = createStoreEndpoint(
5
+ "/payments/intents/:id/confirm",
6
+ {
7
+ method: "POST",
8
+ params: z.object({ id: z.string() }),
9
+ },
10
+ async (ctx) => {
11
+ const controller = ctx.context.controllers.payments as PaymentController;
12
+ const intent = await controller.confirmIntent(ctx.params.id);
13
+ if (!intent) return { error: "Payment intent not found", status: 404 };
14
+ return { intent };
15
+ },
16
+ );
@@ -0,0 +1,29 @@
1
+ import { createStoreEndpoint, z } from "@86d-app/core";
2
+ import type { PaymentController } from "../../service";
3
+
4
+ export const createIntent = createStoreEndpoint(
5
+ "/payments/intents",
6
+ {
7
+ method: "POST",
8
+ body: z.object({
9
+ amount: z.number().int().positive(),
10
+ currency: z.string().max(3).optional(),
11
+ email: z.string().email().optional(),
12
+ orderId: z.string().optional(),
13
+ checkoutSessionId: z.string().optional(),
14
+ metadata: z.record(z.string(), z.unknown()).optional(),
15
+ }),
16
+ },
17
+ async (ctx) => {
18
+ const controller = ctx.context.controllers.payments as PaymentController;
19
+ const intent = await controller.createIntent({
20
+ amount: ctx.body.amount,
21
+ currency: ctx.body.currency,
22
+ email: ctx.body.email,
23
+ orderId: ctx.body.orderId,
24
+ checkoutSessionId: ctx.body.checkoutSessionId,
25
+ metadata: ctx.body.metadata,
26
+ });
27
+ return { intent };
28
+ },
29
+ );
@@ -0,0 +1,17 @@
1
+ import { createStoreEndpoint, z } from "@86d-app/core";
2
+ import type { PaymentController } from "../../service";
3
+
4
+ export const deletePaymentMethod = createStoreEndpoint(
5
+ "/payments/methods/:id",
6
+ {
7
+ method: "DELETE",
8
+ params: z.object({ id: z.string() }),
9
+ },
10
+ async (ctx) => {
11
+ const controller = ctx.context.controllers.payments as PaymentController;
12
+ const method = await controller.getPaymentMethod(ctx.params.id);
13
+ if (!method) return { error: "Payment method not found", status: 404 };
14
+ const deleted = await controller.deletePaymentMethod(ctx.params.id);
15
+ return { deleted };
16
+ },
17
+ );
@@ -0,0 +1,16 @@
1
+ import { createStoreEndpoint, z } from "@86d-app/core";
2
+ import type { PaymentController } from "../../service";
3
+
4
+ export const getIntent = createStoreEndpoint(
5
+ "/payments/intents/:id",
6
+ {
7
+ method: "GET",
8
+ params: z.object({ id: z.string() }),
9
+ },
10
+ async (ctx) => {
11
+ const controller = ctx.context.controllers.payments as PaymentController;
12
+ const intent = await controller.getIntent(ctx.params.id);
13
+ if (!intent) return { error: "Payment intent not found", status: 404 };
14
+ return { intent };
15
+ },
16
+ );
@@ -0,0 +1,15 @@
1
+ import { cancelIntent } from "./cancel-intent";
2
+ import { confirmIntent } from "./confirm-intent";
3
+ import { createIntent } from "./create-intent";
4
+ import { deletePaymentMethod } from "./delete-method";
5
+ import { getIntent } from "./get-intent";
6
+ import { listPaymentMethods } from "./list-methods";
7
+
8
+ export const storeEndpoints = {
9
+ "/payments/intents": createIntent,
10
+ "/payments/intents/:id": getIntent,
11
+ "/payments/intents/:id/confirm": confirmIntent,
12
+ "/payments/intents/:id/cancel": cancelIntent,
13
+ "/payments/methods": listPaymentMethods,
14
+ "/payments/methods/:id": deletePaymentMethod,
15
+ };