@downcity/services 0.1.6
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 +47 -0
- package/bin/accounts/db.d.ts +19 -0
- package/bin/accounts/db.d.ts.map +1 -0
- package/bin/accounts/db.js +68 -0
- package/bin/accounts/db.js.map +1 -0
- package/bin/accounts/index.d.ts +348 -0
- package/bin/accounts/index.d.ts.map +1 -0
- package/bin/accounts/index.js +681 -0
- package/bin/accounts/index.js.map +1 -0
- package/bin/accounts/oauth.d.ts +129 -0
- package/bin/accounts/oauth.d.ts.map +1 -0
- package/bin/accounts/oauth.js +220 -0
- package/bin/accounts/oauth.js.map +1 -0
- package/bin/accounts/schema.d.ts +319 -0
- package/bin/accounts/schema.d.ts.map +1 -0
- package/bin/accounts/schema.js +72 -0
- package/bin/accounts/schema.js.map +1 -0
- package/bin/balance/index.d.ts +7 -0
- package/bin/balance/index.d.ts.map +1 -0
- package/bin/balance/index.js +6 -0
- package/bin/balance/index.js.map +1 -0
- package/bin/balance/raw.d.ts +20 -0
- package/bin/balance/raw.d.ts.map +1 -0
- package/bin/balance/raw.js +75 -0
- package/bin/balance/raw.js.map +1 -0
- package/bin/balance/routes.d.ts +14 -0
- package/bin/balance/routes.d.ts.map +1 -0
- package/bin/balance/routes.js +166 -0
- package/bin/balance/routes.js.map +1 -0
- package/bin/balance/schema.d.ts +764 -0
- package/bin/balance/schema.d.ts.map +1 -0
- package/bin/balance/schema.js +185 -0
- package/bin/balance/schema.js.map +1 -0
- package/bin/balance/service.d.ts +880 -0
- package/bin/balance/service.d.ts.map +1 -0
- package/bin/balance/service.js +557 -0
- package/bin/balance/service.js.map +1 -0
- package/bin/balance/types.d.ts +326 -0
- package/bin/balance/types.d.ts.map +1 -0
- package/bin/balance/types.js +10 -0
- package/bin/balance/types.js.map +1 -0
- package/bin/balance/utils.d.ts +91 -0
- package/bin/balance/utils.d.ts.map +1 -0
- package/bin/balance/utils.js +231 -0
- package/bin/balance/utils.js.map +1 -0
- package/bin/index.d.ts +22 -0
- package/bin/index.d.ts.map +1 -0
- package/bin/index.js +16 -0
- package/bin/index.js.map +1 -0
- package/bin/payment/index.d.ts +19 -0
- package/bin/payment/index.d.ts.map +1 -0
- package/bin/payment/index.js +63 -0
- package/bin/payment/index.js.map +1 -0
- package/bin/payment/types.d.ts +107 -0
- package/bin/payment/types.d.ts.map +1 -0
- package/bin/payment/types.js +10 -0
- package/bin/payment/types.js.map +1 -0
- package/bin/payment-stripe/index.d.ts +17 -0
- package/bin/payment-stripe/index.d.ts.map +1 -0
- package/bin/payment-stripe/index.js +619 -0
- package/bin/payment-stripe/index.js.map +1 -0
- package/bin/payment-stripe/schema.d.ts +378 -0
- package/bin/payment-stripe/schema.d.ts.map +1 -0
- package/bin/payment-stripe/schema.js +47 -0
- package/bin/payment-stripe/schema.js.map +1 -0
- package/bin/payment-stripe/stripe.d.ts +38 -0
- package/bin/payment-stripe/stripe.d.ts.map +1 -0
- package/bin/payment-stripe/stripe.js +129 -0
- package/bin/payment-stripe/stripe.js.map +1 -0
- package/bin/payment-stripe/types.d.ts +331 -0
- package/bin/payment-stripe/types.d.ts.map +1 -0
- package/bin/payment-stripe/types.js +10 -0
- package/bin/payment-stripe/types.js.map +1 -0
- package/bin/usage/index.d.ts +177 -0
- package/bin/usage/index.d.ts.map +1 -0
- package/bin/usage/index.js +120 -0
- package/bin/usage/index.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Downcity Stripe 一次性充值服务。
|
|
3
|
+
*
|
|
4
|
+
* 关键说明(中文)
|
|
5
|
+
* - 当前版本只处理 Stripe 一次性充值
|
|
6
|
+
* - 不处理 entitlement,也不处理 subscription
|
|
7
|
+
* - 支付成功后统一调用 balance.finishTopup() 完成到账
|
|
8
|
+
*/
|
|
9
|
+
import { stripeEvents, stripePayments } from "./schema.js";
|
|
10
|
+
import { createStripeCheckoutSession, normalizeOptionalText, normalizeRequired, normalizeStripeApiBaseURL, parseStripeWebhookEvent, readMetadata, verifyStripeSignature, } from "./stripe.js";
|
|
11
|
+
/**
|
|
12
|
+
* Stripe 服务对外暴露的运行时环境变量。
|
|
13
|
+
*
|
|
14
|
+
* 关键说明(中文)
|
|
15
|
+
* - secret / webhook 是最常见的宿主注入配置
|
|
16
|
+
* - success / cancel URL 用于 CLI 或纯后端场景下的默认跳转地址
|
|
17
|
+
* - currency / item name / api server URL 提供可选默认值覆写
|
|
18
|
+
*/
|
|
19
|
+
const stripePaymentEnv = [
|
|
20
|
+
{
|
|
21
|
+
key: "STRIPE_SECRET_KEY",
|
|
22
|
+
description: "Stripe secret key,用于创建 Checkout Session",
|
|
23
|
+
required: true,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
key: "STRIPE_WEBHOOK_SECRET",
|
|
27
|
+
description: "Stripe webhook signing secret,用于校验 stripe-signature",
|
|
28
|
+
required: false,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
key: "STRIPE_SUCCESS_URL",
|
|
32
|
+
description: "默认支付成功跳转地址;未在请求里传 success_url 时使用",
|
|
33
|
+
required: false,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
key: "STRIPE_CANCEL_URL",
|
|
37
|
+
description: "默认支付取消跳转地址;未在请求里传 cancel_url 时使用",
|
|
38
|
+
required: false,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
key: "DOWNCITY_INFRA_BASE_URL",
|
|
42
|
+
description: "InfraRuntime 对外访问地址;未显式配置 success/cancel URL 时,会自动生成 Stripe 默认跳转页地址",
|
|
43
|
+
required: false,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
key: "STRIPE_CURRENCY",
|
|
47
|
+
description: "默认结算币种,例如 usd",
|
|
48
|
+
required: false,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
key: "STRIPE_ITEM_NAME",
|
|
52
|
+
description: "Stripe Checkout 展示的默认商品名",
|
|
53
|
+
required: false,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
key: "STRIPE_API_BASE_URL",
|
|
57
|
+
description: "可选的 Stripe API 基础地址覆写,通常只用于测试环境",
|
|
58
|
+
required: false,
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
export { stripeEvents, stripePayments } from "./schema.js";
|
|
62
|
+
/**
|
|
63
|
+
* 创建 Stripe 一次性充值服务。
|
|
64
|
+
*/
|
|
65
|
+
export function stripePaymentService(options) {
|
|
66
|
+
const normalized = normalizeOptions(options);
|
|
67
|
+
return {
|
|
68
|
+
id: "payment.stripe",
|
|
69
|
+
name: "Stripe Payment",
|
|
70
|
+
version: "0.2.0",
|
|
71
|
+
env: stripePaymentEnv,
|
|
72
|
+
schema: {
|
|
73
|
+
payments: stripePayments,
|
|
74
|
+
events: stripeEvents,
|
|
75
|
+
},
|
|
76
|
+
instruction: [
|
|
77
|
+
"使用 Stripe 创建一次性充值 Checkout,并在 webhook 成功后完成 balance topup。",
|
|
78
|
+
"这个服务不处理 entitlement,也不处理 subscription。",
|
|
79
|
+
"当 options 未显式传入时,会回退读取 STRIPE_SECRET_KEY / STRIPE_WEBHOOK_SECRET / STRIPE_SUCCESS_URL / STRIPE_CANCEL_URL。",
|
|
80
|
+
"如果 success/cancel URL 仍未配置,会继续尝试基于 DOWNCITY_INFRA_BASE_URL 自动生成默认跳转页地址。",
|
|
81
|
+
`currency=${normalized.currency},success_url=${normalized.success_url || "read from STRIPE_SUCCESS_URL or route input"}。`,
|
|
82
|
+
"支付成功后统一通过 balance.finishTopup() 完成到账。",
|
|
83
|
+
].join("\n"),
|
|
84
|
+
install(ctx) {
|
|
85
|
+
const payments = ctx.table("payments");
|
|
86
|
+
const events = ctx.table("events");
|
|
87
|
+
const balance = normalized.balance;
|
|
88
|
+
ctx.route({
|
|
89
|
+
method: "POST",
|
|
90
|
+
path: "/checkout/create",
|
|
91
|
+
auth: ["user"],
|
|
92
|
+
async handler(requestCtx) {
|
|
93
|
+
const body = await requestCtx.json();
|
|
94
|
+
const userId = normalizeRequired(requestCtx.user?.user_id, "user_id");
|
|
95
|
+
const topup = await balance.readTopup(normalizeRequired(body.topup_id, "topup_id"));
|
|
96
|
+
if (topup.user_id !== userId) {
|
|
97
|
+
return requestCtx.jsonResponse({ error: "Topup does not belong to current user" }, 403);
|
|
98
|
+
}
|
|
99
|
+
if (topup.status !== "pending") {
|
|
100
|
+
return requestCtx.jsonResponse({ error: `Topup is already ${topup.status}` }, 409);
|
|
101
|
+
}
|
|
102
|
+
const existing = await findActivePaymentByTopup(payments, topup.topup_id);
|
|
103
|
+
if (existing) {
|
|
104
|
+
return requestCtx.jsonResponse(toCheckoutResult(existing));
|
|
105
|
+
}
|
|
106
|
+
const secretKey = normalized.secret_key ?? ctx.env("STRIPE_SECRET_KEY");
|
|
107
|
+
if (!secretKey) {
|
|
108
|
+
return requestCtx.jsonResponse({ error: "Stripe secret key is not configured" }, 500);
|
|
109
|
+
}
|
|
110
|
+
const paymentId = `pay_${randomId()}`;
|
|
111
|
+
const currency = resolveCurrency(normalized, ctx);
|
|
112
|
+
const created = await createStripeCheckoutSession(secretKey, resolveApiBaseURL(normalized, ctx), {
|
|
113
|
+
payment_id: paymentId,
|
|
114
|
+
topup,
|
|
115
|
+
currency,
|
|
116
|
+
success_url: resolveCheckoutRedirectURL({
|
|
117
|
+
value: body.success_url,
|
|
118
|
+
fallback: normalized.success_url ?? ctx.env("STRIPE_SUCCESS_URL"),
|
|
119
|
+
label: "success_url",
|
|
120
|
+
redirectPath: "/v1/payment.stripe/redirect/success",
|
|
121
|
+
ctx,
|
|
122
|
+
request: requestCtx.request,
|
|
123
|
+
}),
|
|
124
|
+
cancel_url: resolveCheckoutRedirectURL({
|
|
125
|
+
value: body.cancel_url,
|
|
126
|
+
fallback: normalized.cancel_url ?? ctx.env("STRIPE_CANCEL_URL"),
|
|
127
|
+
label: "cancel_url",
|
|
128
|
+
redirectPath: "/v1/payment.stripe/redirect/cancel",
|
|
129
|
+
ctx,
|
|
130
|
+
request: requestCtx.request,
|
|
131
|
+
}),
|
|
132
|
+
item_name: resolveItemName(normalized, ctx),
|
|
133
|
+
});
|
|
134
|
+
const now = new Date().toISOString();
|
|
135
|
+
const row = {
|
|
136
|
+
payment_id: paymentId,
|
|
137
|
+
topup_id: topup.topup_id,
|
|
138
|
+
user_id: topup.user_id,
|
|
139
|
+
stripe_checkout_session_id: created.session_id,
|
|
140
|
+
stripe_payment_intent_id: created.payment_intent_id,
|
|
141
|
+
amount: topup.amount,
|
|
142
|
+
currency,
|
|
143
|
+
status: "pending",
|
|
144
|
+
checkout_url: created.checkout_url,
|
|
145
|
+
metadata_json: JSON.stringify({
|
|
146
|
+
unit: topup.unit,
|
|
147
|
+
note: topup.note,
|
|
148
|
+
}),
|
|
149
|
+
created_at: now,
|
|
150
|
+
updated_at: now,
|
|
151
|
+
};
|
|
152
|
+
await payments.insert(row);
|
|
153
|
+
return requestCtx.jsonResponse(toCheckoutResult(row));
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
ctx.route({
|
|
157
|
+
method: "GET",
|
|
158
|
+
path: "/payments/me",
|
|
159
|
+
auth: ["user"],
|
|
160
|
+
async handler(requestCtx) {
|
|
161
|
+
const userId = normalizeRequired(requestCtx.user?.user_id, "user_id");
|
|
162
|
+
const rows = sortPayments(await payments.select({ user_id: userId }));
|
|
163
|
+
return requestCtx.jsonResponse({ items: rows });
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
ctx.route({
|
|
167
|
+
method: "GET",
|
|
168
|
+
path: "/payments",
|
|
169
|
+
auth: ["admin"],
|
|
170
|
+
async handler(requestCtx) {
|
|
171
|
+
return requestCtx.jsonResponse({ items: sortPayments(await payments.select()) });
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
ctx.route({
|
|
175
|
+
method: "GET",
|
|
176
|
+
path: "/events",
|
|
177
|
+
auth: ["admin"],
|
|
178
|
+
async handler(requestCtx) {
|
|
179
|
+
return requestCtx.jsonResponse({ items: sortEvents(await events.select()) });
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
ctx.route({
|
|
183
|
+
method: "POST",
|
|
184
|
+
path: "/webhook",
|
|
185
|
+
auth: [],
|
|
186
|
+
async handler(requestCtx) {
|
|
187
|
+
const raw = await requestCtx.text();
|
|
188
|
+
const webhookSecret = normalized.webhook_secret ?? ctx.env("STRIPE_WEBHOOK_SECRET");
|
|
189
|
+
if (webhookSecret) {
|
|
190
|
+
const signature = requestCtx.request.headers.get("stripe-signature");
|
|
191
|
+
const valid = await verifyStripeSignature(raw, signature, webhookSecret);
|
|
192
|
+
if (!valid)
|
|
193
|
+
return requestCtx.jsonResponse({ error: "Invalid Stripe signature" }, 400);
|
|
194
|
+
}
|
|
195
|
+
const event = parseStripeWebhookEvent(raw);
|
|
196
|
+
const eventId = normalizeRequired(event.id, "stripe event id");
|
|
197
|
+
const eventType = String(event.type ?? "unknown");
|
|
198
|
+
const existing = (await events.select({ event_id: eventId }))[0];
|
|
199
|
+
if (existing) {
|
|
200
|
+
return requestCtx.jsonResponse({
|
|
201
|
+
received: true,
|
|
202
|
+
event_id: eventId,
|
|
203
|
+
sync_status: existing.sync_status,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
const eventRow = {
|
|
207
|
+
event_id: eventId,
|
|
208
|
+
type: eventType,
|
|
209
|
+
payload_json: JSON.stringify(event),
|
|
210
|
+
sync_status: "pending",
|
|
211
|
+
sync_error: "",
|
|
212
|
+
created_at: new Date().toISOString(),
|
|
213
|
+
};
|
|
214
|
+
await events.insert(eventRow);
|
|
215
|
+
try {
|
|
216
|
+
const syncStatus = await syncStripeEvent({
|
|
217
|
+
event,
|
|
218
|
+
payments,
|
|
219
|
+
balance,
|
|
220
|
+
});
|
|
221
|
+
await updateEvent(events, eventId, syncStatus, "");
|
|
222
|
+
return requestCtx.jsonResponse({
|
|
223
|
+
received: true,
|
|
224
|
+
event_id: eventId,
|
|
225
|
+
sync_status: syncStatus,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
230
|
+
await updateEvent(events, eventId, "failed", message);
|
|
231
|
+
return requestCtx.jsonResponse({
|
|
232
|
+
received: true,
|
|
233
|
+
event_id: eventId,
|
|
234
|
+
sync_status: "failed",
|
|
235
|
+
error: message,
|
|
236
|
+
}, 500);
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
ctx.route({
|
|
241
|
+
method: "GET",
|
|
242
|
+
path: "/redirect/success",
|
|
243
|
+
auth: [],
|
|
244
|
+
handler(requestCtx) {
|
|
245
|
+
return htmlResponse(renderRedirectPage({
|
|
246
|
+
title: "Payment successful",
|
|
247
|
+
heading: "Payment completed",
|
|
248
|
+
description: "Your Stripe payment has been accepted. If the balance view has not refreshed yet, close this page and return to your app.",
|
|
249
|
+
request: requestCtx.request,
|
|
250
|
+
}));
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
ctx.route({
|
|
254
|
+
method: "GET",
|
|
255
|
+
path: "/redirect/cancel",
|
|
256
|
+
auth: [],
|
|
257
|
+
handler(requestCtx) {
|
|
258
|
+
return htmlResponse(renderRedirectPage({
|
|
259
|
+
title: "Payment canceled",
|
|
260
|
+
heading: "Payment canceled",
|
|
261
|
+
description: "No charge was completed. You can close this page and return to your app to try again later.",
|
|
262
|
+
request: requestCtx.request,
|
|
263
|
+
}));
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* 统一同步 Stripe webhook 事件。
|
|
271
|
+
*/
|
|
272
|
+
async function syncStripeEvent(input) {
|
|
273
|
+
const { event, payments, balance } = input;
|
|
274
|
+
switch (String(event.type ?? "")) {
|
|
275
|
+
case "checkout.session.completed":
|
|
276
|
+
return await syncCheckoutCompleted(event, payments, balance);
|
|
277
|
+
case "checkout.session.expired":
|
|
278
|
+
return await syncCheckoutExpired(event, payments);
|
|
279
|
+
case "payment_intent.payment_failed":
|
|
280
|
+
return await syncPaymentIntentFailed(event, payments);
|
|
281
|
+
default:
|
|
282
|
+
return "ignored";
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* 同步 `checkout.session.completed`。
|
|
287
|
+
*/
|
|
288
|
+
async function syncCheckoutCompleted(event, payments, balance) {
|
|
289
|
+
const object = readMetadata(event.data?.object);
|
|
290
|
+
const payment = await findPaymentByEventObject(payments, object, "stripe_checkout_session_id");
|
|
291
|
+
if (!payment)
|
|
292
|
+
return "ignored";
|
|
293
|
+
if (payment.status === "paid")
|
|
294
|
+
return "applied";
|
|
295
|
+
const paymentIntentId = normalizeOptionalText(object.payment_intent);
|
|
296
|
+
const topup = await balance.readTopup(payment.topup_id);
|
|
297
|
+
if (topup.status === "pending") {
|
|
298
|
+
await balance.finishTopup(payment.topup_id, {
|
|
299
|
+
note: "stripe topup",
|
|
300
|
+
ref: normalizeOptionalText(object.id) || payment.stripe_checkout_session_id,
|
|
301
|
+
meta: {
|
|
302
|
+
stripe_event_id: normalizeOptionalText(event.id),
|
|
303
|
+
stripe_checkout_session_id: normalizeOptionalText(object.id),
|
|
304
|
+
stripe_payment_intent_id: paymentIntentId,
|
|
305
|
+
stripe_payment_id: payment.payment_id,
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
await updatePayment(payments, payment.payment_id, {
|
|
310
|
+
status: "paid",
|
|
311
|
+
stripe_payment_intent_id: paymentIntentId || payment.stripe_payment_intent_id,
|
|
312
|
+
});
|
|
313
|
+
return "applied";
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* 同步 `checkout.session.expired`。
|
|
317
|
+
*/
|
|
318
|
+
async function syncCheckoutExpired(event, payments) {
|
|
319
|
+
const object = readMetadata(event.data?.object);
|
|
320
|
+
const payment = await findPaymentByEventObject(payments, object, "stripe_checkout_session_id");
|
|
321
|
+
if (!payment)
|
|
322
|
+
return "ignored";
|
|
323
|
+
if (payment.status !== "pending")
|
|
324
|
+
return "ignored";
|
|
325
|
+
await updatePayment(payments, payment.payment_id, { status: "expired" });
|
|
326
|
+
return "applied";
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* 同步 `payment_intent.payment_failed`。
|
|
330
|
+
*/
|
|
331
|
+
async function syncPaymentIntentFailed(event, payments) {
|
|
332
|
+
const object = readMetadata(event.data?.object);
|
|
333
|
+
const payment = await findPaymentByEventObject(payments, object, "stripe_payment_intent_id");
|
|
334
|
+
if (!payment)
|
|
335
|
+
return "ignored";
|
|
336
|
+
if (payment.status !== "pending")
|
|
337
|
+
return "ignored";
|
|
338
|
+
await updatePayment(payments, payment.payment_id, {
|
|
339
|
+
status: "failed",
|
|
340
|
+
stripe_payment_intent_id: normalizeOptionalText(object.id) || payment.stripe_payment_intent_id,
|
|
341
|
+
});
|
|
342
|
+
return "applied";
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* 根据 webhook 对象寻找支付记录。
|
|
346
|
+
*/
|
|
347
|
+
async function findPaymentByEventObject(payments, object, primaryField) {
|
|
348
|
+
const metadata = readMetadata(object.metadata);
|
|
349
|
+
const paymentId = normalizeOptionalText(metadata.payment_id);
|
|
350
|
+
if (paymentId) {
|
|
351
|
+
const record = (await payments.select({ payment_id: paymentId }))[0];
|
|
352
|
+
if (record)
|
|
353
|
+
return record;
|
|
354
|
+
}
|
|
355
|
+
const directId = normalizeOptionalText(object.id);
|
|
356
|
+
if (directId) {
|
|
357
|
+
const record = (await payments.select({ [primaryField]: directId }))[0];
|
|
358
|
+
if (record)
|
|
359
|
+
return record;
|
|
360
|
+
}
|
|
361
|
+
const topupId = normalizeOptionalText(metadata.topup_id) || normalizeOptionalText(object.client_reference_id);
|
|
362
|
+
if (topupId) {
|
|
363
|
+
return await findActivePaymentByTopup(payments, topupId);
|
|
364
|
+
}
|
|
365
|
+
return undefined;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* 查询某个 topup 当前的活跃支付记录。
|
|
369
|
+
*/
|
|
370
|
+
async function findActivePaymentByTopup(payments, topupId) {
|
|
371
|
+
const rows = sortPayments(await payments.select({ topup_id: topupId }));
|
|
372
|
+
return rows.find((row) => row.status === "pending");
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* 更新支付记录。
|
|
376
|
+
*/
|
|
377
|
+
async function updatePayment(payments, paymentId, input) {
|
|
378
|
+
await payments.update({
|
|
379
|
+
where: { payment_id: paymentId },
|
|
380
|
+
values: {
|
|
381
|
+
status: input.status,
|
|
382
|
+
stripe_payment_intent_id: normalizeOptionalText(input.stripe_payment_intent_id),
|
|
383
|
+
updated_at: new Date().toISOString(),
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* 更新 webhook 同步状态。
|
|
389
|
+
*/
|
|
390
|
+
async function updateEvent(events, eventId, syncStatus, syncError) {
|
|
391
|
+
await events.update({
|
|
392
|
+
where: { event_id: eventId },
|
|
393
|
+
values: {
|
|
394
|
+
sync_status: syncStatus,
|
|
395
|
+
sync_error: syncError.trim(),
|
|
396
|
+
},
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* 将支付记录裁剪为创建 Checkout 的返回结构。
|
|
401
|
+
*/
|
|
402
|
+
function toCheckoutResult(row) {
|
|
403
|
+
return {
|
|
404
|
+
payment_id: row.payment_id,
|
|
405
|
+
topup_id: row.topup_id,
|
|
406
|
+
stripe_checkout_session_id: row.stripe_checkout_session_id,
|
|
407
|
+
checkout_url: row.checkout_url,
|
|
408
|
+
status: row.status,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* 统一排序支付记录。
|
|
413
|
+
*/
|
|
414
|
+
function sortPayments(rows) {
|
|
415
|
+
return [...rows].sort((left, right) => {
|
|
416
|
+
if (left.updated_at === right.updated_at)
|
|
417
|
+
return right.created_at.localeCompare(left.created_at);
|
|
418
|
+
return right.updated_at.localeCompare(left.updated_at);
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* 统一排序 webhook 事件记录。
|
|
423
|
+
*/
|
|
424
|
+
function sortEvents(rows) {
|
|
425
|
+
return [...rows].sort((left, right) => right.created_at.localeCompare(left.created_at));
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* 选择跳转地址。
|
|
429
|
+
*/
|
|
430
|
+
function pickRedirectURL(value, fallback, label) {
|
|
431
|
+
const candidate = normalizeOptionalText(value);
|
|
432
|
+
if (candidate)
|
|
433
|
+
return candidate;
|
|
434
|
+
return normalizeRequired(fallback, label);
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* 解析 Checkout 跳转地址。
|
|
438
|
+
*
|
|
439
|
+
* 关键说明(中文)
|
|
440
|
+
* - 优先使用请求级显式传入值
|
|
441
|
+
* - 再回退到服务配置或 runtime env
|
|
442
|
+
* - 再回退到 DOWNCITY_INFRA_BASE_URL
|
|
443
|
+
* - 最后使用当前请求 origin 自动生成服务内置结果页
|
|
444
|
+
*/
|
|
445
|
+
function resolveCheckoutRedirectURL(input) {
|
|
446
|
+
const configured = normalizeOptionalText(input.fallback);
|
|
447
|
+
const fromBaseURL = buildBaseURLRedirect(input.ctx.env("DOWNCITY_INFRA_BASE_URL"), input.redirectPath);
|
|
448
|
+
const fromRequestOrigin = buildRequestOriginRedirect(input.request, input.redirectPath);
|
|
449
|
+
return pickRedirectURL(input.value, configured || fromBaseURL || fromRequestOrigin, input.label);
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* 规范化服务配置。
|
|
453
|
+
*/
|
|
454
|
+
function normalizeOptions(options) {
|
|
455
|
+
if (!options?.balance)
|
|
456
|
+
throw new TypeError("Stripe payment service requires a balance service instance");
|
|
457
|
+
return {
|
|
458
|
+
...options,
|
|
459
|
+
balance: options.balance,
|
|
460
|
+
currency: normalizeCurrency(options.currency) || "usd",
|
|
461
|
+
item_name: normalizeOptionalText(options.item_name) || "Downcity Topup",
|
|
462
|
+
api_base_url: normalizeStripeApiBaseURL(options.api_base_url),
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* 规范化币种。
|
|
467
|
+
*/
|
|
468
|
+
function normalizeCurrency(value) {
|
|
469
|
+
return normalizeOptionalText(value).toLowerCase();
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* 解析当前请求最终使用的币种。
|
|
473
|
+
*/
|
|
474
|
+
function resolveCurrency(options, ctx) {
|
|
475
|
+
return normalizeCurrency(ctx.env("STRIPE_CURRENCY")) || options.currency || "usd";
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* 解析当前请求最终使用的 Checkout 商品名。
|
|
479
|
+
*/
|
|
480
|
+
function resolveItemName(options, ctx) {
|
|
481
|
+
return normalizeOptionalText(ctx.env("STRIPE_ITEM_NAME")) || options.item_name;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* 解析当前请求最终使用的 Stripe API 基础地址。
|
|
485
|
+
*/
|
|
486
|
+
function resolveApiBaseURL(options, ctx) {
|
|
487
|
+
return normalizeStripeApiBaseURL(ctx.env("STRIPE_API_BASE_URL") || options.api_base_url);
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* 基于 InfraRuntime 对外地址生成默认跳转页 URL。
|
|
491
|
+
*/
|
|
492
|
+
function buildBaseURLRedirect(baseURL, path) {
|
|
493
|
+
const normalizedBaseURL = normalizeOptionalText(baseURL).replace(/\/+$/, "");
|
|
494
|
+
if (!normalizedBaseURL)
|
|
495
|
+
return "";
|
|
496
|
+
return `${normalizedBaseURL}${path}`;
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* 基于当前请求 origin 生成默认跳转页 URL。
|
|
500
|
+
*
|
|
501
|
+
* 关键说明(中文)
|
|
502
|
+
* - 多域名或 workers.dev / 自定义域混用时,当前请求 origin 往往最准确
|
|
503
|
+
* - 这里保留为最后兜底,避免开发者必须手动配置 BASE URL
|
|
504
|
+
*/
|
|
505
|
+
function buildRequestOriginRedirect(request, path) {
|
|
506
|
+
try {
|
|
507
|
+
return new URL(path, request.url).toString();
|
|
508
|
+
}
|
|
509
|
+
catch {
|
|
510
|
+
return "";
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* 返回最小 HTML 页面,避免 Stripe 跳回后出现 404。
|
|
515
|
+
*/
|
|
516
|
+
function htmlResponse(html) {
|
|
517
|
+
return new Response(html, {
|
|
518
|
+
status: 200,
|
|
519
|
+
headers: { "content-type": "text/html; charset=utf-8" },
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* 渲染支付完成/取消结果页。
|
|
524
|
+
*/
|
|
525
|
+
function renderRedirectPage(input) {
|
|
526
|
+
const homeURL = escapeHTML(new URL("/", input.request.url).toString());
|
|
527
|
+
const title = escapeHTML(input.title);
|
|
528
|
+
const heading = escapeHTML(input.heading);
|
|
529
|
+
const description = escapeHTML(input.description);
|
|
530
|
+
return `<!doctype html>
|
|
531
|
+
<html lang="en">
|
|
532
|
+
<head>
|
|
533
|
+
<meta charset="utf-8" />
|
|
534
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
535
|
+
<title>${title}</title>
|
|
536
|
+
<style>
|
|
537
|
+
:root {
|
|
538
|
+
color-scheme: light;
|
|
539
|
+
--bg: #f5f7fb;
|
|
540
|
+
--card: #ffffff;
|
|
541
|
+
--text: #142033;
|
|
542
|
+
--muted: #5a6a85;
|
|
543
|
+
--border: #d9e2f1;
|
|
544
|
+
--accent: #1f6feb;
|
|
545
|
+
}
|
|
546
|
+
* { box-sizing: border-box; }
|
|
547
|
+
body {
|
|
548
|
+
margin: 0;
|
|
549
|
+
min-height: 100vh;
|
|
550
|
+
display: grid;
|
|
551
|
+
place-items: center;
|
|
552
|
+
padding: 24px;
|
|
553
|
+
background:
|
|
554
|
+
radial-gradient(circle at top, #e9f1ff 0, rgba(233, 241, 255, 0) 42%),
|
|
555
|
+
linear-gradient(180deg, #f8fbff 0%, var(--bg) 100%);
|
|
556
|
+
color: var(--text);
|
|
557
|
+
font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
558
|
+
}
|
|
559
|
+
main {
|
|
560
|
+
width: min(100%, 560px);
|
|
561
|
+
padding: 32px;
|
|
562
|
+
border: 1px solid var(--border);
|
|
563
|
+
border-radius: 20px;
|
|
564
|
+
background: var(--card);
|
|
565
|
+
box-shadow: 0 18px 60px rgba(16, 24, 40, 0.08);
|
|
566
|
+
}
|
|
567
|
+
h1 {
|
|
568
|
+
margin: 0 0 12px;
|
|
569
|
+
font-size: 28px;
|
|
570
|
+
line-height: 1.2;
|
|
571
|
+
}
|
|
572
|
+
p {
|
|
573
|
+
margin: 0;
|
|
574
|
+
color: var(--muted);
|
|
575
|
+
}
|
|
576
|
+
a {
|
|
577
|
+
display: inline-block;
|
|
578
|
+
margin-top: 24px;
|
|
579
|
+
color: #fff;
|
|
580
|
+
background: var(--accent);
|
|
581
|
+
text-decoration: none;
|
|
582
|
+
padding: 12px 16px;
|
|
583
|
+
border-radius: 999px;
|
|
584
|
+
font-weight: 600;
|
|
585
|
+
}
|
|
586
|
+
</style>
|
|
587
|
+
</head>
|
|
588
|
+
<body>
|
|
589
|
+
<main>
|
|
590
|
+
<h1>${heading}</h1>
|
|
591
|
+
<p>${description}</p>
|
|
592
|
+
<a href="${homeURL}">Return to Downcity</a>
|
|
593
|
+
</main>
|
|
594
|
+
</body>
|
|
595
|
+
</html>`;
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* 最小 HTML 转义,避免文本直接拼进页面时破坏结构。
|
|
599
|
+
*/
|
|
600
|
+
function escapeHTML(value) {
|
|
601
|
+
return value
|
|
602
|
+
.replaceAll("&", "&")
|
|
603
|
+
.replaceAll("<", "<")
|
|
604
|
+
.replaceAll(">", ">")
|
|
605
|
+
.replaceAll('"', """)
|
|
606
|
+
.replaceAll("'", "'");
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* 生成随机 ID。
|
|
610
|
+
*/
|
|
611
|
+
function randomId() {
|
|
612
|
+
const buffer = new Uint8Array(12);
|
|
613
|
+
crypto.getRandomValues(buffer);
|
|
614
|
+
return btoa(String.fromCharCode(...buffer))
|
|
615
|
+
.replace(/\+/g, "-")
|
|
616
|
+
.replace(/\//g, "_")
|
|
617
|
+
.replace(/=+$/, "");
|
|
618
|
+
}
|
|
619
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/payment-stripe/index.ts"],"names":[],"mappings":"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"}
|