@youidian/sdk 3.3.10 → 3.4.2
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/dist/client.cjs +1123 -41
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +74 -4
- package/dist/client.d.ts +74 -4
- package/dist/client.js +1120 -41
- package/dist/client.js.map +1 -1
- package/dist/feedback.cjs +682 -0
- package/dist/feedback.cjs.map +1 -0
- package/dist/feedback.d.cts +38 -0
- package/dist/feedback.d.ts +38 -0
- package/dist/feedback.js +656 -0
- package/dist/feedback.js.map +1 -0
- package/dist/index.cjs +1866 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1861 -41
- package/dist/index.js.map +1 -1
- package/dist/{login-DerOcXcH.d.cts → login-Dqemys65.d.cts} +8 -4
- package/dist/{login-DerOcXcH.d.ts → login-Dqemys65.d.ts} +8 -4
- package/dist/login.cjs +334 -33
- package/dist/login.cjs.map +1 -1
- package/dist/login.d.cts +1 -1
- package/dist/login.d.ts +1 -1
- package/dist/login.js +334 -33
- package/dist/login.js.map +1 -1
- package/dist/server.cjs +87 -0
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +146 -1
- package/dist/server.d.ts +146 -1
- package/dist/server.js +87 -0
- package/dist/server.js.map +1 -1
- package/package.json +6 -1
package/dist/server.js
CHANGED
|
@@ -131,6 +131,12 @@ var PaymentClient = class {
|
|
|
131
131
|
}
|
|
132
132
|
/**
|
|
133
133
|
* Decrypts the callback notification payload using AES-256-GCM.
|
|
134
|
+
*
|
|
135
|
+
* @deprecated This legacy encrypted-envelope protocol is NOT what the platform
|
|
136
|
+
* actually delivers. The platform sends an HMAC-signed plaintext JSON webhook
|
|
137
|
+
* with `X-UniPay-Signature` / `X-UniPay-Timestamp` headers; use
|
|
138
|
+
* {@link PaymentClient.verifyWebhookSignature} to validate those instead.
|
|
139
|
+
*
|
|
134
140
|
* @param notification - The encrypted notification from payment webhook
|
|
135
141
|
* @returns Decrypted payment callback data
|
|
136
142
|
*/
|
|
@@ -153,6 +159,39 @@ var PaymentClient = class {
|
|
|
153
159
|
);
|
|
154
160
|
}
|
|
155
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Verify an inbound platform webhook signature.
|
|
164
|
+
*
|
|
165
|
+
* The platform delivers webhooks as plaintext JSON with two headers:
|
|
166
|
+
* `X-UniPay-Signature` (HMAC-SHA256 of `${timestamp}.${rawBodyString}`) and
|
|
167
|
+
* `X-UniPay-Timestamp`. The body itself is NOT encrypted; verify the signature
|
|
168
|
+
* then `JSON.parse` the body to obtain a {@link WebhookPayload}.
|
|
169
|
+
*
|
|
170
|
+
* @param rawBody - The raw request body string (exactly as received)
|
|
171
|
+
* @param signature - The `X-UniPay-Signature` header value (hex)
|
|
172
|
+
* @param timestamp - The `X-UniPay-Timestamp` header value (ms epoch string)
|
|
173
|
+
* @param options - Optional `toleranceMs` for timestamp freshness (default 5 min)
|
|
174
|
+
* @returns `true` if the signature is valid and the timestamp is fresh
|
|
175
|
+
*/
|
|
176
|
+
verifyWebhookSignature(rawBody, signature, timestamp, options) {
|
|
177
|
+
try {
|
|
178
|
+
const toleranceMs = options?.toleranceMs ?? 5 * 60 * 1e3;
|
|
179
|
+
const timestampMs = Number.parseInt(timestamp, 10);
|
|
180
|
+
if (Number.isNaN(timestampMs) || Math.abs(Date.now() - timestampMs) > toleranceMs) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
const rawString = `${timestamp}.${rawBody}`;
|
|
184
|
+
const expected = crypto.createHmac("sha256", this.appSecret).update(rawString).digest("hex");
|
|
185
|
+
const signatureBuffer = Buffer.from(signature);
|
|
186
|
+
const expectedBuffer = Buffer.from(expected);
|
|
187
|
+
if (signatureBuffer.length !== expectedBuffer.length) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
return crypto.timingSafeEqual(signatureBuffer, expectedBuffer);
|
|
191
|
+
} catch {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
156
195
|
/**
|
|
157
196
|
* Fetch products for the configured app.
|
|
158
197
|
*/
|
|
@@ -354,6 +393,27 @@ var PaymentClient = class {
|
|
|
354
393
|
...options
|
|
355
394
|
});
|
|
356
395
|
}
|
|
396
|
+
/**
|
|
397
|
+
* Consume numeric entitlements from an ordered key pool.
|
|
398
|
+
* Useful when subscription credits and perpetual credits use separate keys.
|
|
399
|
+
*/
|
|
400
|
+
async consumeEntitlementPool(userId, keys, amount, options) {
|
|
401
|
+
return this.request("POST", `/users/${userId}/entitlements/consume-pool`, {
|
|
402
|
+
keys,
|
|
403
|
+
amount,
|
|
404
|
+
...options
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Get active numeric entitlement buckets ordered by expiration.
|
|
409
|
+
*/
|
|
410
|
+
async getEntitlementCreditBuckets(userId, keys) {
|
|
411
|
+
const query = keys && keys.length > 0 ? `?keys=${encodeURIComponent(keys.join(","))}` : "";
|
|
412
|
+
return this.request(
|
|
413
|
+
"GET",
|
|
414
|
+
`/users/${userId}/entitlements/credit-buckets${query}`
|
|
415
|
+
);
|
|
416
|
+
}
|
|
357
417
|
/**
|
|
358
418
|
* Add numeric entitlement (e.g. refund)
|
|
359
419
|
* @param userId - User ID
|
|
@@ -434,6 +494,33 @@ var PaymentClient = class {
|
|
|
434
494
|
}
|
|
435
495
|
);
|
|
436
496
|
}
|
|
497
|
+
/**
|
|
498
|
+
* Create a hosted WeChat official account binding URL for a user.
|
|
499
|
+
*/
|
|
500
|
+
async createWechatMessageBinding(params) {
|
|
501
|
+
const loginExternalUserId = params.loginExternalUserId?.trim() || params.userId?.trim();
|
|
502
|
+
const merchantUserId = params.merchantUserId?.trim();
|
|
503
|
+
if (!loginExternalUserId && !merchantUserId) {
|
|
504
|
+
throw new Error("userId or merchantUserId is required");
|
|
505
|
+
}
|
|
506
|
+
return this.request("POST", "/messages/wechat/bindings", {
|
|
507
|
+
...params,
|
|
508
|
+
loginExternalUserId,
|
|
509
|
+
merchantUserId
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Send an app-linked message template to a bound recipient.
|
|
514
|
+
*/
|
|
515
|
+
async sendMessage(params) {
|
|
516
|
+
if (!params.templateId?.trim() && !params.templateCode?.trim()) {
|
|
517
|
+
throw new Error("templateId or templateCode is required");
|
|
518
|
+
}
|
|
519
|
+
if (!params.data || typeof params.data !== "object") {
|
|
520
|
+
throw new Error("data is required");
|
|
521
|
+
}
|
|
522
|
+
return this.request("POST", "/messages/send", params);
|
|
523
|
+
}
|
|
437
524
|
};
|
|
438
525
|
export {
|
|
439
526
|
PaymentClient,
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["/**\n * Youidian Payment SDK - Server Module\n * 用于服务端集成,包含签名、订单创建、回调解密等功能\n */\n\nimport crypto from \"crypto\"\n\n/**\n * Order status response\n */\nexport interface OrderStatus {\n\torderId: string\n\tstatus: \"PENDING\" | \"PAID\" | \"CANCELLED\" | \"REFUNDED\" | \"FAILED\"\n\tpaidAt?: string\n\tchannelTransactionId?: string\n}\n\n/**\n * Order details response (full order information)\n */\nexport interface OrderDetails {\n\torderId: string\n\tinternalId: string\n\tstatus: \"PENDING\" | \"PAID\" | \"CANCELLED\" | \"REFUNDED\" | \"FAILED\"\n\tamount: number\n\tcurrency: string\n\tdescription?: string\n\tpaidAt?: string\n\tcreatedAt: string\n\tchannel?: string\n\tmerchantPricing?: MerchantPricingSnapshot\n\tpricingBreakdown?: PricingBreakdown\n\tupgrade?: {\n\t\tisUpgrade: boolean\n\t\tfromProductId?: string | null\n\t\tfromProductCode?: string | null\n\t\tfromPriceId?: string | null\n\t\tfromOrderId?: string | null\n\t\tfromSourceKind?: string | null\n\t\tfromSortOrder?: number | null\n\t\ttoSortOrder?: number | null\n\t\toriginalAmount?: number | null\n\t\tcreditAmount?: number | null\n\t\tfinalPayableAmount?: number | null\n\t\tremainingRatio?: number | null\n\t\tnewPeriodStartsAt?: string | null\n\t\tnewPeriodValue?: number | null\n\t\tnewPeriodUnit?: \"days\" | \"months\" | \"years\" | null\n\t} | null\n\tproduct?: {\n\t\tcode: string\n\t\ttype: string\n\t\tname: string\n\t\tdescription?: string\n\t\tentitlements: ProductEntitlements\n\t\tmetadata?: ProductMetadata | null\n\t}\n}\n\n/**\n * Product Entitlements\n */\nexport interface ProductEntitlements {\n\t[key: string]: any\n}\n\n/**\n * Product Price\n */\nexport interface ProductPrice {\n\tid: string\n\tcurrency: string\n\tamount: number\n\tdisplayAmount: string\n\tlocale: string | null\n\tisDefault: boolean\n}\n\nexport interface ProductSubscriptionPeriod {\n\tvalue: number\n\tunit: \"days\" | \"months\" | \"years\"\n}\n\nexport interface ProductResetRule {\n\tresetInterval: \"month\"\n}\n\nexport interface ProductCustomAmountCurrency {\n\tminAmount: number\n\tmaxAmount: number\n\tstepAmount?: number\n\tunitsPerCurrencyUnit: number\n\tunitsPerCurrencyUnitBasis?: \"MINOR\" | \"MAJOR\"\n}\n\nexport interface ProductCustomAmount {\n\tenabled: boolean\n\tentitlementKey: string\n\tcurrencies: Record<string, ProductCustomAmountCurrency>\n}\n\nexport interface ProductInventory {\n\tenabled: boolean\n\ttotalQuantity: number\n\treserveTimeoutSeconds?: number\n}\n\nexport interface ProductMetadata {\n\tsubscriptionPeriod?: ProductSubscriptionPeriod\n\texpiringEntitlements?: string[]\n\tresetEntitlements?: Record<string, ProductResetRule>\n\tautoAssignOnNewUser?: boolean\n\ttrialDurationDays?: number\n\tcustomAmount?: ProductCustomAmount\n\tinventory?: ProductInventory\n}\n\nexport type ProductStockLookupMode = \"auto\" | \"id\" | \"code\"\n\nexport interface ProductStock {\n\tproductId: string\n\tproductCode: string\n\tlimited: boolean\n\ttotal: number | null\n\treserved: number\n\tsold: number\n\tavailable: number | null\n\tupdatedAt: string\n\treserveTimeoutSeconds: number | null\n}\n\nexport interface ProductStockQueryOptions {\n\tlookupBy?: ProductStockLookupMode\n\tlocale?: string\n\tcurrency?: string\n}\n\nexport interface ProductStocksQueryParams {\n\tproductIds?: string[]\n\tproductCodes?: string[]\n}\n\nexport interface PricingBreakdown {\n\tisUpgrade: boolean\n\toriginalAmount: number\n\tcreditAmount: number\n\tfinalPayableAmount: number\n\tfromProductCode?: string | null\n}\n\nexport interface ActiveSubscriptionInfo {\n\tproductId: string\n\tproductCode: string\n\tsortOrder: number\n\tpaidAt?: string | null\n\texpiresAt: string\n\tpriceId?: string | null\n\torderId?: string | null\n\tsourceKind?: string | null\n}\n\n/**\n * Product Data\n */\nexport interface Product {\n\tid: string\n\tcode: string\n\ttype: string\n\tname: string\n\tdescription?: string\n\tentitlements: ProductEntitlements\n\tprices: ProductPrice[]\n\tmetadata?: ProductMetadata | null\n}\n\nexport interface CustomAmountRechargeRule extends ProductCustomAmountCurrency {\n\tproductId: string\n\tproductCode: string\n\tentitlementKey: string\n\tcurrency: string\n\tconfiguredMinAmount: number\n\tminimumGrantAmount: number\n}\n\nexport type CustomAmountRechargeValidationResult =\n\t| {\n\t\t\tvalid: true\n\t\t\trule: CustomAmountRechargeRule\n\t }\n\t| {\n\t\t\tvalid: false\n\t\t\tcode:\n\t\t\t\t| \"CUSTOM_AMOUNT_UNAVAILABLE\"\n\t\t\t\t| \"CUSTOM_AMOUNT_INVALID_AMOUNT\"\n\t\t\t\t| \"CUSTOM_AMOUNT_OUT_OF_RANGE\"\n\t\t\t\t| \"CUSTOM_AMOUNT_INVALID_STEP\"\n\t\t\terror: string\n\t\t\trule?: CustomAmountRechargeRule\n\t }\n\n/**\n * Resolve the custom amount recharge rule for a product and currency.\n *\n * Amounts are in the smallest currency unit. `unitsPerCurrencyUnit` defaults\n * to the smallest currency unit. When `unitsPerCurrencyUnitBasis` is `MAJOR`,\n * fractional entitlement amounts are rounded to the nearest whole unit. The\n * returned `minAmount` is the effective lower bound that both satisfies product\n * metadata and grants at least one entitlement unit. `configuredMinAmount`\n * keeps the raw product metadata value for display or diagnostics.\n */\nexport function getCustomAmountRechargeRule(\n\tproduct: Product | null | undefined,\n\tcurrency: string,\n): CustomAmountRechargeRule | null {\n\tif (!product || product.type !== \"CREDIT\") return null\n\tconst normalizedCurrency = currency.trim().toUpperCase()\n\tif (!/^[A-Z]{3}$/.test(normalizedCurrency)) return null\n\n\tconst customAmount = product.metadata?.customAmount\n\tif (customAmount?.enabled !== true) return null\n\n\tconst currencyConfig = customAmount.currencies?.[normalizedCurrency]\n\tif (!currencyConfig) return null\n\n\tconst minimumGrantAmount =\n\t\tcurrencyConfig.unitsPerCurrencyUnitBasis === \"MAJOR\"\n\t\t\t? Math.ceil(50 / currencyConfig.unitsPerCurrencyUnit)\n\t\t\t: Math.ceil(1 / currencyConfig.unitsPerCurrencyUnit)\n\tlet minAmount = Math.max(currencyConfig.minAmount, minimumGrantAmount)\n\tif (currencyConfig.stepAmount && minAmount > currencyConfig.minAmount) {\n\t\tconst stepCount = Math.ceil(\n\t\t\t(minAmount - currencyConfig.minAmount) / currencyConfig.stepAmount,\n\t\t)\n\t\tminAmount = currencyConfig.minAmount + stepCount * currencyConfig.stepAmount\n\t}\n\n\treturn {\n\t\t...currencyConfig,\n\t\tproductId: product.id,\n\t\tproductCode: product.code,\n\t\tentitlementKey: customAmount.entitlementKey,\n\t\tcurrency: normalizedCurrency,\n\t\tconfiguredMinAmount: currencyConfig.minAmount,\n\t\tminimumGrantAmount,\n\t\tminAmount,\n\t}\n}\n\n/**\n * Validate a custom recharge amount before calling `PaymentUI.openPayment`.\n * This is an SDK-side guardrail for integrator-owned amount inputs; the worker\n * still performs authoritative server-side validation when creating the order.\n */\nexport function validateCustomAmountRecharge(\n\tproduct: Product | null | undefined,\n\tcustomAmount: { amount: number; currency: string },\n): CustomAmountRechargeValidationResult {\n\tconst rule = getCustomAmountRechargeRule(product, customAmount.currency)\n\tif (!rule) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\tcode: \"CUSTOM_AMOUNT_UNAVAILABLE\",\n\t\t\terror:\n\t\t\t\t\"Product does not support custom amount recharge for this currency.\",\n\t\t}\n\t}\n\n\tconst amount = Number(customAmount.amount)\n\tif (!Number.isInteger(amount) || amount <= 0) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\tcode: \"CUSTOM_AMOUNT_INVALID_AMOUNT\",\n\t\t\terror:\n\t\t\t\t\"Custom amount must be a positive integer in the smallest currency unit.\",\n\t\t\trule,\n\t\t}\n\t}\n\n\tif (amount < rule.minAmount || amount > rule.maxAmount) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\tcode: \"CUSTOM_AMOUNT_OUT_OF_RANGE\",\n\t\t\terror: `Custom amount must be between ${rule.minAmount} and ${rule.maxAmount}.`,\n\t\t\trule,\n\t\t}\n\t}\n\n\tif (\n\t\trule.stepAmount &&\n\t\t(amount - rule.configuredMinAmount) % rule.stepAmount !== 0\n\t) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\tcode: \"CUSTOM_AMOUNT_INVALID_STEP\",\n\t\t\terror: `Custom amount must follow step ${rule.stepAmount}.`,\n\t\t\trule,\n\t\t}\n\t}\n\n\treturn { valid: true, rule }\n}\n\n/**\n * WeChat JSAPI Payment Parameters (for wx.requestPayment)\n */\nexport interface WechatJsapiPayParams {\n\tappId: string\n\ttimeStamp: string\n\tnonceStr: string\n\tpackage: string\n\tsignType: \"RSA\"\n\tpaySign: string\n}\n\n/**\n * Verified hosted login token payload.\n */\nexport interface VerifiedLoginToken {\n\tappId: string\n\tuserId: string\n\tlegacyCasdoorId?: string | null\n\tchannel: string\n\temail?: string | null\n\tname?: string | null\n\tusername?: string | null\n\tavatar?: string | null\n\tphoneCountryCode?: string | null\n\tphoneNumber?: string | null\n\tphoneE164?: string | null\n\tphoneVerifiedAt?: string | null\n\twechatOpenId?: string | null\n\twechatUnionId?: string | null\n\texpiresAt: string\n}\n\nexport interface SendPhoneVerificationCodeParams {\n\tuserId: string\n\tphoneCountryCode?: string\n\tcountryCode?: string\n\tphoneNumber: string\n}\n\nexport interface SendPhoneVerificationCodeResponse {\n\tphoneCountryCode?: string | null\n\tphoneNumber?: string | null\n\tphoneE164: string\n\texpiresAt: string\n\tcooldownSeconds?: number\n\tresendAfterSeconds?: number\n}\n\nexport interface BindPhoneNumberParams {\n\tuserId: string\n\tphoneCountryCode?: string\n\tcountryCode?: string\n\tphoneNumber: string\n\tcode: string\n}\n\nexport interface BindPhoneNumberResponse {\n\tuser: {\n\t\tuserId: string\n\t\tphoneCountryCode?: string | null\n\t\tphoneNumber?: string | null\n\t\tphoneE164?: string | null\n\t\tphoneVerifiedAt?: string | null\n\t}\n\tmerged: boolean\n\tmergeSummary?: Record<string, any>\n}\n\n/**\n * SDK Client Options\n */\nexport interface PaymentClientOptions {\n\t/** Application ID (Required) */\n\tappId: string\n\t/** Application Secret (Required for server-side operations) */\n\tappSecret: string\n\n\t/**\n\t * @deprecated Use apiUrl and checkoutUrl instead\n\t * API Base URL (e.g. https://pay.youidian.com)\n\t * If apiUrl or checkoutUrl is not provided, this will be used as fallback\n\t * Default: https://pay.imgto.link\n\t */\n\tbaseUrl?: string\n\n\t/**\n\t * API server URL for backend requests (e.g. https://api.youidian.com)\n\t * Default: https://pay-api.imgto.link\n\t */\n\tapiUrl?: string\n\n\t/**\n\t * Checkout page URL for client-side payment (e.g. https://pay.youidian.com)\n\t * Default: https://pay.imgto.link\n\t */\n\tcheckoutUrl?: string\n}\n\nexport interface MerchantPricingBreakdownItem {\n\ttype: \"coupon\" | \"promotion\" | \"membership\" | \"manual\" | \"other\"\n\tamount: number\n\tlabel?: string\n\tcode?: string\n}\n\nexport interface MerchantPricing {\n\tamount: number\n\tcurrency: string\n\toriginalAmount?: number\n\tdiscountAmount?: number\n\tdiscountReason?: string\n\tdiscountCode?: string\n\tbreakdown?: MerchantPricingBreakdownItem[]\n}\n\nexport interface MerchantPricingSnapshot extends MerchantPricing {\n\toriginalAmount: number\n\tdiscountAmount: number\n\tpriceId: string\n\tproductId: string\n\tproductCode: string\n}\n\n/**\n * Create Order Parameters\n */\nexport interface CreateOrderParams {\n\tproductId?: string\n\tpriceId?: string\n\tchannel?: string\n\tuserId: string\n\treturnUrl?: string\n\tcallbackUrl?: string\n\tmetadata?: Record<string, any>\n\tmerchantOrderId?: string\n\topenid?: string\n\tlocale?: string\n\tcustomAmount?: {\n\t\tamount: number\n\t\tcurrency: string\n\t}\n\tmerchantPricing?: MerchantPricing\n}\n\nexport type CreateBankTransferOrderParams = Omit<CreateOrderParams, \"channel\">\n\n/**\n * Create WeChat Mini Program Order Parameters\n */\nexport type CreateMiniProgramOrderParams = {\n\tuserId: string\n\topenid: string\n\tmerchantOrderId?: string\n\tpriceId: string\n}\n\n/**\n * Create Order Response\n */\nexport interface CreateOrderResponse {\n\torderId: string\n\tinternalId: string\n\tamount: number\n\tcurrency: string\n\tpayParams: any\n\tpricingBreakdown?: PricingBreakdown\n\tstatus?: \"PENDING\" | \"PAID\" | \"CANCELLED\" | \"REFUNDED\" | \"FAILED\"\n\tpaidAt?: string | null\n\tchannel?: string\n\tisSandbox?: boolean\n\tmerchantPricing?: MerchantPricingSnapshot\n}\n\nexport interface CancelOrderResponse {\n\tcancelled: boolean\n\tstatus: \"PENDING\" | \"PAID\" | \"CANCELLED\" | \"REFUNDED\" | \"FAILED\"\n\torderId: string\n\tinternalId?: string | null\n}\n\nexport interface CompleteFreeOrderResponse {\n\tcompleted: boolean\n\tstatus: \"paid\" | \"pending\" | \"cancelled\" | \"refunded\" | \"failed\" | \"unknown\"\n\torderId: string\n\tinternalId?: string | null\n}\n\n/**\n * Payment Callback Notification\n */\nexport interface PaymentNotification {\n\tiv: string\n\tencryptedData: string\n\tauthTag: string\n}\n\n/**\n * Decrypted Payment Callback Data\n */\nexport interface PaymentCallbackData {\n\torderId: string\n\tmerchantOrderId?: string\n\tstatus: \"PAID\" | \"CANCELLED\" | \"REFUNDED\" | \"FAILED\"\n\tamount: number\n\tcurrency: string\n\tpaidAt: string\n\tchannelTransactionId?: string\n\tmetadata?: Record<string, any>\n}\n\n/**\n * Get Orders Parameters\n */\nexport interface GetOrdersParams {\n\tpage?: number\n\tpageSize?: number\n\tuserId?: string\n\tstatus?: \"PENDING\" | \"PAID\" | \"CANCELLED\" | \"REFUNDED\" | \"FAILED\"\n\tstartDate?: string\n\tendDate?: string\n}\n\n/**\n * Order List Item\n */\nexport interface OrderListItem {\n\torderId: string\n\tinternalId: string\n\tmerchantUserId: string\n\tstatus: \"PENDING\" | \"PAID\" | \"CANCELLED\" | \"REFUNDED\" | \"FAILED\"\n\tamount: number\n\tcurrency: string\n\tchannel?: string\n\tpaidAt?: string\n\tcreatedAt: string\n}\n\n/**\n * Get Orders Response\n */\nexport interface GetOrdersResponse {\n\torders: OrderListItem[]\n\tpagination: {\n\t\ttotal: number\n\t\tpage: number\n\t\tpageSize: number\n\t\ttotalPages: number\n\t}\n}\n\n/**\n * Entitlement Detail Item\n */\nexport interface EntitlementDetailItem {\n\ttype: string\n\tcurrent: number | boolean\n\tlimit?: number\n\texpiresAt?: string | null\n\tresetInterval?: string | null\n\tnextResetAt?: string | null\n\tsourceKind?: string | null\n}\n\n/**\n * Entitlement Detail - returned by getEntitlementsDetail\n */\nexport type EntitlementDetail = Record<string, EntitlementDetailItem>\n\n/**\n * Ensure User With Trial Response\n */\nexport interface EnsureUserWithTrialResponse {\n\tisNew: boolean\n\ttrialAssigned: boolean\n\ttrialProductCode?: string\n\tentitlements: EntitlementDetail\n}\n\n/**\n * Server-side Payment Client\n * 服务端支付客户端,用于创建订单、查询状态、解密回调\n */\nexport class PaymentClient {\n\tprivate readonly appId: string\n\tprivate readonly appSecret: string\n\tprivate readonly apiUrl: string // 用于 API 调用\n\tprivate readonly checkoutUrl: string // 用于生成 checkout URL\n\n\tconstructor(options: PaymentClientOptions) {\n\t\tif (!options.appId) throw new Error(\"appId is required\")\n\t\tif (!options.appSecret) throw new Error(\"appSecret is required\")\n\n\t\tthis.appId = options.appId\n\t\tthis.appSecret = options.appSecret\n\n\t\t// apiUrl: 优先使用 apiUrl,其次 baseUrl,默认 https://pay-api.imgto.link\n\t\tconst apiUrl =\n\t\t\toptions.apiUrl || options.baseUrl || \"https://pay-api.imgto.link\"\n\t\tthis.apiUrl = apiUrl.replace(/\\/$/, \"\") // Remove trailing slash\n\n\t\t// checkoutUrl: 优先使用 checkoutUrl,其次 baseUrl,默认 https://pay.imgto.link\n\t\tconst checkoutUrl =\n\t\t\toptions.checkoutUrl || options.baseUrl || \"https://pay.imgto.link\"\n\t\tthis.checkoutUrl = checkoutUrl.replace(/\\/$/, \"\") // Remove trailing slash\n\t}\n\n\t/**\n\t * Generate SHA256 signature for the request\n\t * Logic: SHA256(appId + appSecret + timestamp)\n\t */\n\tprivate generateSignature(timestamp: number): string {\n\t\tconst str = `${this.appId}${this.appSecret}${timestamp}`\n\t\treturn crypto.createHash(\"sha256\").update(str).digest(\"hex\")\n\t}\n\n\t/**\n\t * Internal request helper for Gateway API\n\t */\n\tprivate async request<T>(\n\t\tmethod: string,\n\t\tpath: string,\n\t\tbody?: any,\n\t): Promise<T> {\n\t\tconst timestamp = Date.now()\n\t\tconst signature = this.generateSignature(timestamp)\n\n\t\tconst url = `${this.apiUrl}/api/v1/gateway/${this.appId}${path}`\n\n\t\tconst headers: HeadersInit = {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\"X-Pay-Timestamp\": timestamp.toString(),\n\t\t\t\"X-Pay-Sign\": signature,\n\t\t}\n\n\t\tconst options: RequestInit = {\n\t\t\tmethod,\n\t\t\theaders,\n\t\t\tbody: body ? JSON.stringify(body) : undefined,\n\t\t}\n\n\t\tconst response = await fetch(url, options)\n\n\t\tif (!response.ok) {\n\t\t\tconst errorText = await response.text()\n\t\t\tlet parsedError: any = null\n\t\t\ttry {\n\t\t\t\tparsedError = JSON.parse(errorText)\n\t\t\t} catch {}\n\t\t\tconst message =\n\t\t\t\tparsedError?.message ||\n\t\t\t\tparsedError?.error ||\n\t\t\t\terrorText ||\n\t\t\t\t\"Request failed\"\n\t\t\tconst error = new Error(message)\n\t\t\t;(error as Error & { status?: number; code?: string }).status =\n\t\t\t\tresponse.status\n\t\t\t;(error as Error & { status?: number; code?: string }).code =\n\t\t\t\tparsedError?.code || parsedError?.error || undefined\n\t\t\tthrow error\n\t\t}\n\n\t\tconst json = await response.json()\n\t\tif (json.error) {\n\t\t\tthrow new Error(`Payment API Error: ${json.error}`)\n\t\t}\n\n\t\treturn json.data as T\n\t}\n\n\t/**\n\t * Decrypts the callback notification payload using AES-256-GCM.\n\t * @param notification - The encrypted notification from payment webhook\n\t * @returns Decrypted payment callback data\n\t */\n\tdecryptCallback(notification: PaymentNotification): PaymentCallbackData {\n\t\ttry {\n\t\t\tconst { iv, encryptedData, authTag } = notification\n\t\t\tconst key = crypto.createHash(\"sha256\").update(this.appSecret).digest()\n\t\t\tconst decipher = crypto.createDecipheriv(\n\t\t\t\t\"aes-256-gcm\",\n\t\t\t\tkey,\n\t\t\t\tBuffer.from(iv, \"hex\"),\n\t\t\t)\n\n\t\t\tdecipher.setAuthTag(Buffer.from(authTag, \"hex\"))\n\n\t\t\tlet decrypted = decipher.update(encryptedData, \"hex\", \"utf8\")\n\t\t\tdecrypted += decipher.final(\"utf8\")\n\n\t\t\treturn JSON.parse(decrypted)\n\t\t} catch {\n\t\t\tthrow new Error(\n\t\t\t\t\"Failed to decrypt payment callback: Invalid secret or tampered data.\",\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Fetch products for the configured app.\n\t */\n\tasync getProducts(options?: {\n\t\tlocale?: string\n\t\tcurrency?: string\n\t}): Promise<Product[]> {\n\t\tconst params = new URLSearchParams()\n\t\tif (options?.locale) params.append(\"locale\", options.locale)\n\t\tif (options?.currency) params.append(\"currency\", options.currency)\n\n\t\tconst path = params.toString()\n\t\t\t? `/products?${params.toString()}`\n\t\t\t: \"/products\"\n\t\treturn this.request(\"GET\", path)\n\t}\n\n\t/**\n\t * Fetch the realtime stock snapshot for a single product.\n\t */\n\tasync getProductStock(\n\t\tproductIdOrCode: string,\n\t\toptions?: ProductStockQueryOptions,\n\t): Promise<ProductStock> {\n\t\tconst value = productIdOrCode?.trim()\n\t\tif (!value) {\n\t\t\tthrow new Error(\"productIdOrCode is required\")\n\t\t}\n\n\t\tconst params = new URLSearchParams()\n\t\tparams.set(\"productIdOrCode\", value)\n\t\tif (options?.lookupBy) params.set(\"lookupBy\", options.lookupBy)\n\t\tif (options?.locale) params.set(\"locale\", options.locale)\n\t\tif (options?.currency) params.set(\"currency\", options.currency)\n\n\t\treturn this.request(\n\t\t\t\"GET\",\n\t\t\t`/products/${encodeURIComponent(value)}/stock?${params.toString()}`,\n\t\t)\n\t}\n\n\t/**\n\t * Fetch realtime stock snapshots for multiple products.\n\t */\n\tasync getProductStocks(\n\t\tparams: ProductStocksQueryParams,\n\t\toptions?: ProductStockQueryOptions,\n\t): Promise<ProductStock[]>\n\tasync getProductStocks(\n\t\tparams: ProductStocksQueryParams & ProductStockQueryOptions,\n\t): Promise<ProductStock[]>\n\tasync getProductStocks(\n\t\tparams: ProductStocksQueryParams & ProductStockQueryOptions,\n\t\toptions?: ProductStockQueryOptions,\n\t): Promise<ProductStock[]> {\n\t\tconst productIds = [\n\t\t\t...new Set(\n\t\t\t\t(params.productIds || []).map((item) => item.trim()).filter(Boolean),\n\t\t\t),\n\t\t]\n\t\tconst productCodes = [\n\t\t\t...new Set(\n\t\t\t\t(params.productCodes || []).map((item) => item.trim()).filter(Boolean),\n\t\t\t),\n\t\t]\n\t\tif (productIds.length === 0 && productCodes.length === 0) {\n\t\t\tthrow new Error(\"productIds or productCodes is required\")\n\t\t}\n\n\t\tconst queryOptions = options || params\n\t\tconst query = new URLSearchParams()\n\t\tif (productIds.length > 0) query.set(\"productIds\", productIds.join(\",\"))\n\t\tif (productCodes.length > 0)\n\t\t\tquery.set(\"productCodes\", productCodes.join(\",\"))\n\t\tif (queryOptions.lookupBy) query.set(\"lookupBy\", queryOptions.lookupBy)\n\t\tif (queryOptions.locale) query.set(\"locale\", queryOptions.locale)\n\t\tif (queryOptions.currency) query.set(\"currency\", queryOptions.currency)\n\n\t\treturn this.request(\"GET\", `/products/stocks?${query.toString()}`)\n\t}\n\n\t/**\n\t * Create a new order\n\t * @param params - Order creation parameters\n\t * @returns Order details with payment parameters\n\t */\n\tasync createOrder(params: CreateOrderParams): Promise<CreateOrderResponse> {\n\t\treturn this.request(\"POST\", \"/orders\", params)\n\t}\n\n\t/**\n\t * Create a paid manual bank transfer order.\n\t * @param params - Order creation parameters without channel\n\t * @returns Paid order details\n\t */\n\tasync createBankTransferOrder(\n\t\tparams: CreateBankTransferOrderParams,\n\t): Promise<CreateOrderResponse> {\n\t\treturn this.request(\"POST\", \"/orders\", {\n\t\t\t...params,\n\t\t\tchannel: \"BANK_TRANSFER\",\n\t\t} satisfies CreateOrderParams)\n\t}\n\n\t/**\n\t * Create a WeChat Mini Program order (channel fixed to WECHAT_MINI)\n\t * @param params - Mini program order parameters\n\t * @returns Order details with payment parameters\n\t */\n\tasync createMiniProgramOrder(\n\t\tparams: CreateMiniProgramOrderParams,\n\t): Promise<CreateOrderResponse> {\n\t\tconst { openid, ...rest } = params\n\t\treturn this.request(\"POST\", \"/orders\", {\n\t\t\t...rest,\n\t\t\tchannel: \"WECHAT_MINI\",\n\t\t\topenid,\n\t\t\tmetadata: { openid },\n\t\t} satisfies CreateOrderParams)\n\t}\n\n\t/**\n\t * Pay for an existing order\n\t * @param orderId - The order ID to pay\n\t * @param params - Payment parameters including channel\n\t */\n\tasync payOrder(\n\t\torderId: string,\n\t\tparams: {\n\t\t\tchannel: string\n\t\t\treturnUrl?: string\n\t\t\topenid?: string\n\t\t\t[key: string]: any\n\t\t},\n\t): Promise<CreateOrderResponse> {\n\t\treturn this.request(\"POST\", `/orders/${orderId}/pay`, params)\n\t}\n\n\t/**\n\t * Cancel a pending order and release its inventory reservation.\n\t * Paid/refunded/failed orders are returned unchanged by the gateway.\n\t */\n\tasync cancelOrder(\n\t\torderId: string,\n\t\tparams?: { reason?: string },\n\t): Promise<CancelOrderResponse> {\n\t\tconst value = orderId?.trim()\n\t\tif (!value) {\n\t\t\tthrow new Error(\"orderId is required\")\n\t\t}\n\n\t\treturn this.request(\n\t\t\t\"POST\",\n\t\t\t`/orders/${encodeURIComponent(value)}/cancel`,\n\t\t\tparams || {},\n\t\t)\n\t}\n\n\t/**\n\t * Complete a zero-amount FREE order.\n\t * This marks the pending order as paid and triggers normal paid-order side effects.\n\t */\n\tasync completeFreeOrder(orderId: string): Promise<CompleteFreeOrderResponse> {\n\t\tconst value = orderId?.trim()\n\t\tif (!value) {\n\t\t\tthrow new Error(\"orderId is required\")\n\t\t}\n\n\t\treturn this.request(\n\t\t\t\"POST\",\n\t\t\t`/orders/${encodeURIComponent(value)}/free/complete`,\n\t\t\t{},\n\t\t)\n\t}\n\n\t/**\n\t * Query order status\n\t * @param orderId - The order ID to query\n\t */\n\tasync getOrderStatus(orderId: string): Promise<OrderStatus> {\n\t\treturn this.request(\"GET\", `/orders/${orderId}`)\n\t}\n\n\t/**\n\t * Get order details (full order information)\n\t * @param orderId - The order ID to query\n\t */\n\tasync getOrderDetails(orderId: string): Promise<OrderDetails> {\n\t\treturn this.request(\"GET\", `/orders/${orderId}/details`)\n\t}\n\n\t/**\n\t * Get orders list with pagination\n\t * @param params - Query parameters (pagination, filters)\n\t * @returns Orders list and pagination info\n\t */\n\tasync getOrders(params?: GetOrdersParams): Promise<GetOrdersResponse> {\n\t\tconst queryParams = new URLSearchParams()\n\t\tif (params?.page) queryParams.append(\"page\", params.page.toString())\n\t\tif (params?.pageSize)\n\t\t\tqueryParams.append(\"pageSize\", params.pageSize.toString())\n\t\tif (params?.userId) queryParams.append(\"userId\", params.userId)\n\t\tif (params?.status) queryParams.append(\"status\", params.status)\n\t\tif (params?.startDate) queryParams.append(\"startDate\", params.startDate)\n\t\tif (params?.endDate) queryParams.append(\"endDate\", params.endDate)\n\n\t\tconst path = queryParams.toString()\n\t\t\t? `/orders?${queryParams.toString()}`\n\t\t\t: \"/orders\"\n\t\treturn this.request<GetOrdersResponse>(\"GET\", path)\n\t}\n\n\t/**\n\t * Get user entitlements in the legacy flat shape.\n\t * @param userId - User ID\n\t */\n\tasync getEntitlements(userId: string): Promise<Record<string, any>> {\n\t\treturn this.request(\"GET\", `/users/${userId}/entitlements`)\n\t}\n\n\t/**\n\t * Get user entitlements with full details (type, expiry, reset config, source)\n\t * @param userId - User ID\n\t */\n\tasync getEntitlementsDetail(userId: string): Promise<EntitlementDetail> {\n\t\treturn this.request(\"GET\", `/users/${userId}/entitlements/detail`)\n\t}\n\n\tasync getActiveSubscription(\n\t\tuserId: string,\n\t): Promise<ActiveSubscriptionInfo | null> {\n\t\treturn this.request(\"GET\", `/users/${userId}/active-subscription`)\n\t}\n\n\t/**\n\t * Ensure user exists and auto-assign trial product if new user\n\t * This should be called when user first logs in or registers\n\t * @param userId - User ID\n\t */\n\tasync ensureUserWithTrial(\n\t\tuserId: string,\n\t): Promise<EnsureUserWithTrialResponse> {\n\t\treturn this.request(\"POST\", `/users/${userId}/entitlements/bootstrap`, {})\n\t}\n\n\t/**\n\t * Get a single entitlement value\n\t * @param userId - User ID\n\t * @param key - Entitlement key\n\t */\n\tasync getEntitlementValue(userId: string, key: string): Promise<any> {\n\t\tconst entitlements = await this.getEntitlements(userId)\n\t\treturn entitlements[key] ?? null\n\t}\n\n\t/**\n\t * Consume numeric entitlement\n\t * @param userId - User ID\n\t * @param key - Entitlement key\n\t * @param amount - Amount to consume\n\t */\n\tasync consumeEntitlement(\n\t\tuserId: string,\n\t\tkey: string,\n\t\tamount: number,\n\t\toptions?: {\n\t\t\tidempotencyKey?: string\n\t\t\tmetadata?: Record<string, any>\n\t\t},\n\t): Promise<{ balance: number }> {\n\t\treturn this.request(\"POST\", `/users/${userId}/entitlements/consume`, {\n\t\t\tkey,\n\t\t\tamount,\n\t\t\t...options,\n\t\t})\n\t}\n\n\t/**\n\t * Add numeric entitlement (e.g. refund)\n\t * @param userId - User ID\n\t * @param key - Entitlement key\n\t * @param amount - Amount to add\n\t */\n\tasync addEntitlement(\n\t\tuserId: string,\n\t\tkey: string,\n\t\tamount: number,\n\t): Promise<{ balance: number }> {\n\t\treturn this.request(\"POST\", `/users/${userId}/entitlements/add`, {\n\t\t\tkey,\n\t\t\tamount,\n\t\t})\n\t}\n\n\t/**\n\t * Toggle boolean entitlement\n\t * @param userId - User ID\n\t * @param key - Entitlement key\n\t * @param enabled - Whether to enable\n\t */\n\tasync toggleEntitlement(\n\t\tuserId: string,\n\t\tkey: string,\n\t\tenabled: boolean,\n\t): Promise<{ isEnabled: boolean }> {\n\t\t// Toggle endpoint expects POST with enabled flag\n\t\t// However, looking at list_dir, we have toggle/route.ts\n\t\t// I should verify its contract, but assuming standard toggle pattern:\n\t\treturn this.request(\"POST\", `/users/${userId}/entitlements/toggle`, {\n\t\t\tkey,\n\t\t\tenabled,\n\t\t})\n\t}\n\n\t/**\n\t * Generate checkout URL for client-side payment\n\t * @param productId - Product ID\n\t * @param priceId - Price ID\n\t * @returns Checkout page URL\n\t */\n\tgetCheckoutUrl(productId: string, priceId: string): string {\n\t\treturn `${this.checkoutUrl}/checkout/${this.appId}/${productId}/${priceId}`\n\t}\n\n\t/**\n\t * Verify a hosted login token and return the normalized login profile.\n\t * This request is signed with your app credentials and routed through the worker API.\n\t */\n\tasync verifyLoginToken(token: string): Promise<VerifiedLoginToken> {\n\t\tif (!token?.trim()) {\n\t\t\tthrow new Error(\"login token is required\")\n\t\t}\n\t\treturn this.request(\"POST\", \"/login/tokens/verify\", { token: token.trim() })\n\t}\n\n\t/**\n\t * Send a phone verification code for binding a phone number to a hosted login user.\n\t */\n\tasync sendPhoneVerificationCode(\n\t\tparams: SendPhoneVerificationCodeParams,\n\t): Promise<SendPhoneVerificationCodeResponse> {\n\t\tconst userId = params.userId?.trim()\n\t\tconst phoneNumber = params.phoneNumber?.trim()\n\t\tif (!userId) throw new Error(\"userId is required\")\n\t\tif (!phoneNumber) throw new Error(\"phoneNumber is required\")\n\n\t\treturn this.request(\n\t\t\t\"POST\",\n\t\t\t`/login/users/${encodeURIComponent(userId)}/phone/code`,\n\t\t\t{\n\t\t\t\tphoneCountryCode: params.phoneCountryCode || params.countryCode,\n\t\t\t\tphoneNumber,\n\t\t\t},\n\t\t)\n\t}\n\n\t/**\n\t * Bind a verified phone number to a hosted login user, merging existing accounts when needed.\n\t */\n\tasync bindPhoneNumber(\n\t\tparams: BindPhoneNumberParams,\n\t): Promise<BindPhoneNumberResponse> {\n\t\tconst userId = params.userId?.trim()\n\t\tconst phoneNumber = params.phoneNumber?.trim()\n\t\tconst code = params.code?.trim()\n\t\tif (!userId) throw new Error(\"userId is required\")\n\t\tif (!phoneNumber) throw new Error(\"phoneNumber is required\")\n\t\tif (!code) throw new Error(\"code is required\")\n\n\t\treturn this.request(\n\t\t\t\"POST\",\n\t\t\t`/login/users/${encodeURIComponent(userId)}/phone/bind`,\n\t\t\t{\n\t\t\t\tcode,\n\t\t\t\tphoneCountryCode: params.phoneCountryCode || params.countryCode,\n\t\t\t\tphoneNumber,\n\t\t\t},\n\t\t)\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["/**\n * Youidian Payment SDK - Server Module\n * 用于服务端集成,包含签名、订单创建、回调解密等功能\n */\n\nimport crypto from \"crypto\"\n\n/**\n * Order status response\n */\nexport interface OrderStatus {\n\torderId: string\n\tstatus: \"PENDING\" | \"PAID\" | \"CANCELLED\" | \"REFUNDED\" | \"FAILED\"\n\tpaidAt?: string\n\tchannelTransactionId?: string\n}\n\n/**\n * Order details response (full order information)\n */\nexport interface OrderDetails {\n\torderId: string\n\tinternalId: string\n\tstatus: \"PENDING\" | \"PAID\" | \"CANCELLED\" | \"REFUNDED\" | \"FAILED\"\n\tamount: number\n\tcurrency: string\n\tdescription?: string\n\tpaidAt?: string\n\tcreatedAt: string\n\tchannel?: string\n\tmerchantPricing?: MerchantPricingSnapshot\n\tpricingBreakdown?: PricingBreakdown\n\tupgrade?: {\n\t\tisUpgrade: boolean\n\t\tfromProductId?: string | null\n\t\tfromProductCode?: string | null\n\t\tfromPriceId?: string | null\n\t\tfromOrderId?: string | null\n\t\tfromSourceKind?: string | null\n\t\tfromSortOrder?: number | null\n\t\ttoSortOrder?: number | null\n\t\toriginalAmount?: number | null\n\t\tcreditAmount?: number | null\n\t\tfinalPayableAmount?: number | null\n\t\tremainingRatio?: number | null\n\t\tnewPeriodStartsAt?: string | null\n\t\tnewPeriodValue?: number | null\n\t\tnewPeriodUnit?: \"days\" | \"months\" | \"years\" | null\n\t} | null\n\tproduct?: {\n\t\tcode: string\n\t\ttype: string\n\t\tname: string\n\t\tdescription?: string\n\t\tentitlements: ProductEntitlements\n\t\tmetadata?: ProductMetadata | null\n\t}\n}\n\n/**\n * Product Entitlements\n */\nexport interface ProductEntitlements {\n\t[key: string]: any\n}\n\n/**\n * Product Price\n */\nexport interface ProductPrice {\n\tid: string\n\tcurrency: string\n\tamount: number\n\tdisplayAmount: string\n\tlocale: string | null\n\tisDefault: boolean\n}\n\nexport interface ProductSubscriptionPeriod {\n\tvalue: number\n\tunit: \"days\" | \"months\" | \"years\"\n}\n\nexport interface ProductResetRule {\n\tresetInterval: \"month\"\n}\n\nexport interface ProductCustomAmountCurrency {\n\tminAmount: number\n\tmaxAmount: number\n\tstepAmount?: number\n\tunitsPerCurrencyUnit: number\n\tunitsPerCurrencyUnitBasis?: \"MINOR\" | \"MAJOR\"\n}\n\nexport interface ProductCustomAmount {\n\tenabled: boolean\n\tentitlementKey: string\n\tcurrencies: Record<string, ProductCustomAmountCurrency>\n}\n\nexport interface ProductInventory {\n\tenabled: boolean\n\ttotalQuantity: number\n\treserveTimeoutSeconds?: number\n}\n\nexport interface ProductMetadata {\n\tsubscriptionPeriod?: ProductSubscriptionPeriod\n\tsubscriptionGroup?: string\n\trestrictSubscriptionPurchase?: boolean\n\texpiringEntitlements?: string[]\n\tresetEntitlements?: Record<string, ProductResetRule>\n\tautoAssignOnNewUser?: boolean\n\ttrialDurationDays?: number\n\tcustomAmount?: ProductCustomAmount\n\tinventory?: ProductInventory\n}\n\nexport type ProductStockLookupMode = \"auto\" | \"id\" | \"code\"\n\nexport interface ProductStock {\n\tproductId: string\n\tproductCode: string\n\tlimited: boolean\n\ttotal: number | null\n\treserved: number\n\tsold: number\n\tavailable: number | null\n\tupdatedAt: string\n\treserveTimeoutSeconds: number | null\n}\n\nexport interface ProductStockQueryOptions {\n\tlookupBy?: ProductStockLookupMode\n\tlocale?: string\n\tcurrency?: string\n}\n\nexport interface ProductStocksQueryParams {\n\tproductIds?: string[]\n\tproductCodes?: string[]\n}\n\nexport interface PricingBreakdown {\n\tisUpgrade: boolean\n\toriginalAmount: number\n\tcreditAmount: number\n\tfinalPayableAmount: number\n\tfromProductCode?: string | null\n}\n\nexport interface ConsumeEntitlementPoolResult {\n\tbalance: number\n\tbalances: Record<string, number>\n\tconsumed: Record<string, number>\n\tconsumedBuckets?: EntitlementCreditBucketConsumption[]\n}\n\nexport interface EntitlementCreditBucket {\n\tkey: string\n\tgrantId: string | null\n\tsourceKind: string\n\tsourceProductId: string | null\n\tsourceOrderId: string | null\n\tamount: number\n\tremaining: number\n\texpiresAt: string | null\n\tgrantedAt: string | null\n\tmetadata?: Record<string, any> | null\n}\n\nexport interface EntitlementCreditBucketConsumption {\n\tkey: string\n\tgrantId: string | null\n\tamount: number\n\texpiresAt: string | null\n\tsourceProductId: string | null\n\tsourceOrderId: string | null\n}\n\nexport interface ActiveSubscriptionInfo {\n\tproductId: string\n\tproductCode: string\n\tsortOrder: number\n\tpaidAt?: string | null\n\texpiresAt: string\n\tpriceId?: string | null\n\torderId?: string | null\n\tsourceKind?: string | null\n}\n\n/**\n * Product Data\n */\nexport interface Product {\n\tid: string\n\tcode: string\n\ttype: string\n\tname: string\n\tdescription?: string\n\tentitlements: ProductEntitlements\n\tprices: ProductPrice[]\n\tmetadata?: ProductMetadata | null\n}\n\nexport interface CustomAmountRechargeRule extends ProductCustomAmountCurrency {\n\tproductId: string\n\tproductCode: string\n\tentitlementKey: string\n\tcurrency: string\n\tconfiguredMinAmount: number\n\tminimumGrantAmount: number\n}\n\nexport type CustomAmountRechargeValidationResult =\n\t| {\n\t\t\tvalid: true\n\t\t\trule: CustomAmountRechargeRule\n\t }\n\t| {\n\t\t\tvalid: false\n\t\t\tcode:\n\t\t\t\t| \"CUSTOM_AMOUNT_UNAVAILABLE\"\n\t\t\t\t| \"CUSTOM_AMOUNT_INVALID_AMOUNT\"\n\t\t\t\t| \"CUSTOM_AMOUNT_OUT_OF_RANGE\"\n\t\t\t\t| \"CUSTOM_AMOUNT_INVALID_STEP\"\n\t\t\terror: string\n\t\t\trule?: CustomAmountRechargeRule\n\t }\n\n/**\n * Resolve the custom amount recharge rule for a product and currency.\n *\n * Amounts are in the smallest currency unit. `unitsPerCurrencyUnit` defaults\n * to the smallest currency unit. When `unitsPerCurrencyUnitBasis` is `MAJOR`,\n * fractional entitlement amounts are rounded to the nearest whole unit. The\n * returned `minAmount` is the effective lower bound that both satisfies product\n * metadata and grants at least one entitlement unit. `configuredMinAmount`\n * keeps the raw product metadata value for display or diagnostics.\n */\nexport function getCustomAmountRechargeRule(\n\tproduct: Product | null | undefined,\n\tcurrency: string,\n): CustomAmountRechargeRule | null {\n\tif (!product || product.type !== \"CREDIT\") return null\n\tconst normalizedCurrency = currency.trim().toUpperCase()\n\tif (!/^[A-Z]{3}$/.test(normalizedCurrency)) return null\n\n\tconst customAmount = product.metadata?.customAmount\n\tif (customAmount?.enabled !== true) return null\n\n\tconst currencyConfig = customAmount.currencies?.[normalizedCurrency]\n\tif (!currencyConfig) return null\n\n\tconst minimumGrantAmount =\n\t\tcurrencyConfig.unitsPerCurrencyUnitBasis === \"MAJOR\"\n\t\t\t? Math.ceil(50 / currencyConfig.unitsPerCurrencyUnit)\n\t\t\t: Math.ceil(1 / currencyConfig.unitsPerCurrencyUnit)\n\tlet minAmount = Math.max(currencyConfig.minAmount, minimumGrantAmount)\n\tif (currencyConfig.stepAmount && minAmount > currencyConfig.minAmount) {\n\t\tconst stepCount = Math.ceil(\n\t\t\t(minAmount - currencyConfig.minAmount) / currencyConfig.stepAmount,\n\t\t)\n\t\tminAmount = currencyConfig.minAmount + stepCount * currencyConfig.stepAmount\n\t}\n\n\treturn {\n\t\t...currencyConfig,\n\t\tproductId: product.id,\n\t\tproductCode: product.code,\n\t\tentitlementKey: customAmount.entitlementKey,\n\t\tcurrency: normalizedCurrency,\n\t\tconfiguredMinAmount: currencyConfig.minAmount,\n\t\tminimumGrantAmount,\n\t\tminAmount,\n\t}\n}\n\n/**\n * Validate a custom recharge amount before calling `PaymentUI.openPayment`.\n * This is an SDK-side guardrail for integrator-owned amount inputs; the worker\n * still performs authoritative server-side validation when creating the order.\n */\nexport function validateCustomAmountRecharge(\n\tproduct: Product | null | undefined,\n\tcustomAmount: { amount: number; currency: string },\n): CustomAmountRechargeValidationResult {\n\tconst rule = getCustomAmountRechargeRule(product, customAmount.currency)\n\tif (!rule) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\tcode: \"CUSTOM_AMOUNT_UNAVAILABLE\",\n\t\t\terror:\n\t\t\t\t\"Product does not support custom amount recharge for this currency.\",\n\t\t}\n\t}\n\n\tconst amount = Number(customAmount.amount)\n\tif (!Number.isInteger(amount) || amount <= 0) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\tcode: \"CUSTOM_AMOUNT_INVALID_AMOUNT\",\n\t\t\terror:\n\t\t\t\t\"Custom amount must be a positive integer in the smallest currency unit.\",\n\t\t\trule,\n\t\t}\n\t}\n\n\tif (amount < rule.minAmount || amount > rule.maxAmount) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\tcode: \"CUSTOM_AMOUNT_OUT_OF_RANGE\",\n\t\t\terror: `Custom amount must be between ${rule.minAmount} and ${rule.maxAmount}.`,\n\t\t\trule,\n\t\t}\n\t}\n\n\tif (\n\t\trule.stepAmount &&\n\t\t(amount - rule.configuredMinAmount) % rule.stepAmount !== 0\n\t) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\tcode: \"CUSTOM_AMOUNT_INVALID_STEP\",\n\t\t\terror: `Custom amount must follow step ${rule.stepAmount}.`,\n\t\t\trule,\n\t\t}\n\t}\n\n\treturn { valid: true, rule }\n}\n\n/**\n * WeChat JSAPI Payment Parameters (for wx.requestPayment)\n */\nexport interface WechatJsapiPayParams {\n\tappId: string\n\ttimeStamp: string\n\tnonceStr: string\n\tpackage: string\n\tsignType: \"RSA\"\n\tpaySign: string\n}\n\n/**\n * Verified hosted login token payload.\n */\nexport interface VerifiedLoginToken {\n\tappId: string\n\tuserId: string\n\tlegacyCasdoorId?: string | null\n\tchannel: string\n\temail?: string | null\n\temailVerified?: boolean | null\n\tname?: string | null\n\tusername?: string | null\n\tavatar?: string | null\n\tphoneCountryCode?: string | null\n\tphoneNumber?: string | null\n\tphoneE164?: string | null\n\tphoneVerifiedAt?: string | null\n\twechatOpenId?: string | null\n\twechatUnionId?: string | null\n\texpiresAt: string\n}\n\nexport interface SendPhoneVerificationCodeParams {\n\tuserId: string\n\tphoneCountryCode?: string\n\tcountryCode?: string\n\tphoneNumber: string\n}\n\nexport interface SendPhoneVerificationCodeResponse {\n\tphoneCountryCode?: string | null\n\tphoneNumber?: string | null\n\tphoneE164: string\n\texpiresAt: string\n\tcooldownSeconds?: number\n\tresendAfterSeconds?: number\n}\n\nexport interface BindPhoneNumberParams {\n\tuserId: string\n\tphoneCountryCode?: string\n\tcountryCode?: string\n\tphoneNumber: string\n\tcode: string\n}\n\nexport interface BindPhoneNumberResponse {\n\tuser: {\n\t\tuserId: string\n\t\tphoneCountryCode?: string | null\n\t\tphoneNumber?: string | null\n\t\tphoneE164?: string | null\n\t\tphoneVerifiedAt?: string | null\n\t}\n\tmerged: boolean\n\tmergeSummary?: Record<string, any>\n}\n\nexport interface CreateWechatMessageBindingParams {\n\t/** Hosted login external user ID returned by Youidian login. */\n\tuserId?: string\n\tloginExternalUserId?: string\n\t/** Integrator-owned user ID when hosted login is not used. */\n\tmerchantUserId?: string\n\t/** Optional linked template ID or code to infer the WeChat channel. */\n\ttemplateId?: string\n\ttemplateCode?: string\n\t/** Optional direct channel ID when no template is selected yet. */\n\tchannelId?: string\n\t/** Optional locale for the hosted binding page. */\n\tlocale?: string\n\t/** Optional URL to return to after binding succeeds. */\n\treturnUrl?: string\n}\n\nexport interface CreateWechatMessageBindingResponse {\n\tticketId: string\n\tstatus: \"PENDING\" | \"BOUND\" | \"EXPIRED\" | \"FAILED\"\n\tbindingUrl: string\n\tqrCodeUrl?: string | null\n\texpiresAt: string\n}\n\nexport type MessageTemplateDataValue =\n\t| string\n\t| number\n\t| boolean\n\t| {\n\t\t\tvalue: string | number | boolean\n\t\t\tcolor?: string\n\t }\n\nexport interface SendMessageParams {\n\ttemplateId?: string\n\ttemplateCode?: string\n\tuserId?: string\n\tloginExternalUserId?: string\n\tmerchantUserId?: string\n\tdata: Record<string, MessageTemplateDataValue> | MessageTemplateDataValue[]\n\turl?: string\n\tredirectUrl?: string\n\tminiProgram?: {\n\t\tappid: string\n\t\tpagepath?: string\n\t}\n\tidempotencyKey?: string\n}\n\nexport interface SendMessageResponse {\n\tlogId: string\n\tstatus: \"PENDING\" | \"SENT\" | \"FAILED\" | \"SKIPPED\"\n\tproviderMessageId?: string | null\n\terrorCode?: string | null\n\terrorMessage?: string | null\n\tbindRequired?: boolean\n\tidempotent?: boolean\n}\n\n/**\n * SDK Client Options\n */\nexport interface PaymentClientOptions {\n\t/** Application ID (Required) */\n\tappId: string\n\t/** Application Secret (Required for server-side operations) */\n\tappSecret: string\n\n\t/**\n\t * @deprecated Use apiUrl and checkoutUrl instead\n\t * API Base URL (e.g. https://pay.youidian.com)\n\t * If apiUrl or checkoutUrl is not provided, this will be used as fallback\n\t * Default: https://pay.imgto.link\n\t */\n\tbaseUrl?: string\n\n\t/**\n\t * API server URL for backend requests (e.g. https://api.youidian.com)\n\t * Default: https://pay-api.imgto.link\n\t */\n\tapiUrl?: string\n\n\t/**\n\t * Checkout page URL for client-side payment (e.g. https://pay.youidian.com)\n\t * Default: https://pay.imgto.link\n\t */\n\tcheckoutUrl?: string\n}\n\nexport interface MerchantPricingBreakdownItem {\n\ttype: \"coupon\" | \"promotion\" | \"membership\" | \"manual\" | \"other\"\n\tamount: number\n\tlabel?: string\n\tcode?: string\n}\n\nexport interface MerchantPricing {\n\tamount: number\n\tcurrency: string\n\toriginalAmount?: number\n\tdiscountAmount?: number\n\tdiscountReason?: string\n\tdiscountCode?: string\n\tbreakdown?: MerchantPricingBreakdownItem[]\n}\n\nexport interface MerchantPricingSnapshot extends MerchantPricing {\n\toriginalAmount: number\n\tdiscountAmount: number\n\tpriceId: string\n\tproductId: string\n\tproductCode: string\n}\n\n/**\n * Create Order Parameters\n */\nexport interface CreateOrderParams {\n\tproductId?: string\n\tpriceId?: string\n\tchannel?: string\n\tuserId: string\n\treturnUrl?: string\n\tcallbackUrl?: string\n\tmetadata?: Record<string, any>\n\tmerchantOrderId?: string\n\topenid?: string\n\tlocale?: string\n\tcustomAmount?: {\n\t\tamount: number\n\t\tcurrency: string\n\t}\n\tmerchantPricing?: MerchantPricing\n}\n\nexport type CreateBankTransferOrderParams = Omit<CreateOrderParams, \"channel\">\n\n/**\n * Create WeChat Mini Program Order Parameters\n */\nexport type CreateMiniProgramOrderParams = {\n\tuserId: string\n\topenid: string\n\tmerchantOrderId?: string\n\tpriceId: string\n}\n\n/**\n * Create Order Response\n */\nexport interface CreateOrderResponse {\n\torderId: string\n\tinternalId: string\n\tamount: number\n\tcurrency: string\n\tpayParams: any\n\tpricingBreakdown?: PricingBreakdown\n\tstatus?: \"PENDING\" | \"PAID\" | \"CANCELLED\" | \"REFUNDED\" | \"FAILED\"\n\tpaidAt?: string | null\n\tchannel?: string\n\tisSandbox?: boolean\n\tmerchantPricing?: MerchantPricingSnapshot\n}\n\nexport interface CancelOrderResponse {\n\tcancelled: boolean\n\tstatus: \"PENDING\" | \"PAID\" | \"CANCELLED\" | \"REFUNDED\" | \"FAILED\"\n\torderId: string\n\tinternalId?: string | null\n}\n\nexport interface CompleteFreeOrderResponse {\n\tcompleted: boolean\n\tstatus: \"paid\" | \"pending\" | \"cancelled\" | \"refunded\" | \"failed\" | \"unknown\"\n\torderId: string\n\tinternalId?: string | null\n}\n\n/**\n * Payment Callback Notification\n */\nexport interface PaymentNotification {\n\tiv: string\n\tencryptedData: string\n\tauthTag: string\n}\n\n/**\n * Decrypted Payment Callback Data\n */\nexport interface PaymentCallbackData {\n\torderId: string\n\tmerchantOrderId?: string\n\tstatus: \"PAID\" | \"CANCELLED\" | \"REFUNDED\" | \"FAILED\"\n\tamount: number\n\tcurrency: string\n\tpaidAt: string\n\tchannelTransactionId?: string\n\tmetadata?: Record<string, any>\n}\n\n/**\n * Platform webhook payload.\n *\n * The platform delivers this object as plaintext JSON in the webhook request\n * body, signed with `X-UniPay-Signature` / `X-UniPay-Timestamp` headers.\n * Validate with {@link PaymentClient.verifyWebhookSignature} using the raw\n * body string before parsing.\n */\nexport interface WebhookPayload {\n\tevent: \"ORDER_PAID\" | \"ORDER_CANCELLED\" | \"ORDER_REFUNDED\" | \"ORDER_FAILED\"\n\torderId: string\n\tmerchantOrderId: string\n\tmerchantUserId: string\n\tproductCode: string\n\tbilling: unknown\n\tamount: number\n\tcurrency: string\n\tpaidAt: string | null\n\tchannelTransactionId?: string\n\tcancelSubscription?: boolean\n\trevokeEntitlements?: boolean\n}\n\n/**\n * Get Orders Parameters\n */\nexport interface GetOrdersParams {\n\tpage?: number\n\tpageSize?: number\n\tuserId?: string\n\tstatus?: \"PENDING\" | \"PAID\" | \"CANCELLED\" | \"REFUNDED\" | \"FAILED\"\n\tstartDate?: string\n\tendDate?: string\n}\n\n/**\n * Order List Item\n */\nexport interface OrderListItem {\n\torderId: string\n\tinternalId: string\n\tmerchantUserId: string\n\tstatus: \"PENDING\" | \"PAID\" | \"CANCELLED\" | \"REFUNDED\" | \"FAILED\"\n\tamount: number\n\tcurrency: string\n\tchannel?: string\n\tpaidAt?: string\n\tcreatedAt: string\n}\n\n/**\n * Get Orders Response\n */\nexport interface GetOrdersResponse {\n\torders: OrderListItem[]\n\tpagination: {\n\t\ttotal: number\n\t\tpage: number\n\t\tpageSize: number\n\t\ttotalPages: number\n\t}\n}\n\n/**\n * Entitlement Detail Item\n */\nexport interface EntitlementDetailItem {\n\ttype: string\n\tcurrent: number | boolean\n\tlimit?: number\n\texpiresAt?: string | null\n\tresetInterval?: string | null\n\tnextResetAt?: string | null\n\tsourceKind?: string | null\n}\n\n/**\n * Entitlement Detail - returned by getEntitlementsDetail\n */\nexport type EntitlementDetail = Record<string, EntitlementDetailItem>\n\n/**\n * Ensure User With Trial Response\n */\nexport interface EnsureUserWithTrialResponse {\n\tisNew: boolean\n\ttrialAssigned: boolean\n\ttrialProductCode?: string\n\tentitlements: EntitlementDetail\n}\n\n/**\n * Server-side Payment Client\n * 服务端支付客户端,用于创建订单、查询状态、解密回调\n */\nexport class PaymentClient {\n\tprivate readonly appId: string\n\tprivate readonly appSecret: string\n\tprivate readonly apiUrl: string // 用于 API 调用\n\tprivate readonly checkoutUrl: string // 用于生成 checkout URL\n\n\tconstructor(options: PaymentClientOptions) {\n\t\tif (!options.appId) throw new Error(\"appId is required\")\n\t\tif (!options.appSecret) throw new Error(\"appSecret is required\")\n\n\t\tthis.appId = options.appId\n\t\tthis.appSecret = options.appSecret\n\n\t\t// apiUrl: 优先使用 apiUrl,其次 baseUrl,默认 https://pay-api.imgto.link\n\t\tconst apiUrl =\n\t\t\toptions.apiUrl || options.baseUrl || \"https://pay-api.imgto.link\"\n\t\tthis.apiUrl = apiUrl.replace(/\\/$/, \"\") // Remove trailing slash\n\n\t\t// checkoutUrl: 优先使用 checkoutUrl,其次 baseUrl,默认 https://pay.imgto.link\n\t\tconst checkoutUrl =\n\t\t\toptions.checkoutUrl || options.baseUrl || \"https://pay.imgto.link\"\n\t\tthis.checkoutUrl = checkoutUrl.replace(/\\/$/, \"\") // Remove trailing slash\n\t}\n\n\t/**\n\t * Generate SHA256 signature for the request\n\t * Logic: SHA256(appId + appSecret + timestamp)\n\t */\n\tprivate generateSignature(timestamp: number): string {\n\t\tconst str = `${this.appId}${this.appSecret}${timestamp}`\n\t\treturn crypto.createHash(\"sha256\").update(str).digest(\"hex\")\n\t}\n\n\t/**\n\t * Internal request helper for Gateway API\n\t */\n\tprivate async request<T>(\n\t\tmethod: string,\n\t\tpath: string,\n\t\tbody?: any,\n\t): Promise<T> {\n\t\tconst timestamp = Date.now()\n\t\tconst signature = this.generateSignature(timestamp)\n\n\t\tconst url = `${this.apiUrl}/api/v1/gateway/${this.appId}${path}`\n\n\t\tconst headers: HeadersInit = {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\"X-Pay-Timestamp\": timestamp.toString(),\n\t\t\t\"X-Pay-Sign\": signature,\n\t\t}\n\n\t\tconst options: RequestInit = {\n\t\t\tmethod,\n\t\t\theaders,\n\t\t\tbody: body ? JSON.stringify(body) : undefined,\n\t\t}\n\n\t\tconst response = await fetch(url, options)\n\n\t\tif (!response.ok) {\n\t\t\tconst errorText = await response.text()\n\t\t\tlet parsedError: any = null\n\t\t\ttry {\n\t\t\t\tparsedError = JSON.parse(errorText)\n\t\t\t} catch {}\n\t\t\tconst message =\n\t\t\t\tparsedError?.message ||\n\t\t\t\tparsedError?.error ||\n\t\t\t\terrorText ||\n\t\t\t\t\"Request failed\"\n\t\t\tconst error = new Error(message)\n\t\t\t;(error as Error & { status?: number; code?: string }).status =\n\t\t\t\tresponse.status\n\t\t\t;(error as Error & { status?: number; code?: string }).code =\n\t\t\t\tparsedError?.code || parsedError?.error || undefined\n\t\t\tthrow error\n\t\t}\n\n\t\tconst json = await response.json()\n\t\tif (json.error) {\n\t\t\tthrow new Error(`Payment API Error: ${json.error}`)\n\t\t}\n\n\t\treturn json.data as T\n\t}\n\n\t/**\n\t * Decrypts the callback notification payload using AES-256-GCM.\n\t *\n\t * @deprecated This legacy encrypted-envelope protocol is NOT what the platform\n\t * actually delivers. The platform sends an HMAC-signed plaintext JSON webhook\n\t * with `X-UniPay-Signature` / `X-UniPay-Timestamp` headers; use\n\t * {@link PaymentClient.verifyWebhookSignature} to validate those instead.\n\t *\n\t * @param notification - The encrypted notification from payment webhook\n\t * @returns Decrypted payment callback data\n\t */\n\tdecryptCallback(notification: PaymentNotification): PaymentCallbackData {\n\t\ttry {\n\t\t\tconst { iv, encryptedData, authTag } = notification\n\t\t\tconst key = crypto.createHash(\"sha256\").update(this.appSecret).digest()\n\t\t\tconst decipher = crypto.createDecipheriv(\n\t\t\t\t\"aes-256-gcm\",\n\t\t\t\tkey,\n\t\t\t\tBuffer.from(iv, \"hex\"),\n\t\t\t)\n\n\t\t\tdecipher.setAuthTag(Buffer.from(authTag, \"hex\"))\n\n\t\t\tlet decrypted = decipher.update(encryptedData, \"hex\", \"utf8\")\n\t\t\tdecrypted += decipher.final(\"utf8\")\n\n\t\t\treturn JSON.parse(decrypted)\n\t\t} catch {\n\t\t\tthrow new Error(\n\t\t\t\t\"Failed to decrypt payment callback: Invalid secret or tampered data.\",\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Verify an inbound platform webhook signature.\n\t *\n\t * The platform delivers webhooks as plaintext JSON with two headers:\n\t * `X-UniPay-Signature` (HMAC-SHA256 of `${timestamp}.${rawBodyString}`) and\n\t * `X-UniPay-Timestamp`. The body itself is NOT encrypted; verify the signature\n\t * then `JSON.parse` the body to obtain a {@link WebhookPayload}.\n\t *\n\t * @param rawBody - The raw request body string (exactly as received)\n\t * @param signature - The `X-UniPay-Signature` header value (hex)\n\t * @param timestamp - The `X-UniPay-Timestamp` header value (ms epoch string)\n\t * @param options - Optional `toleranceMs` for timestamp freshness (default 5 min)\n\t * @returns `true` if the signature is valid and the timestamp is fresh\n\t */\n\tverifyWebhookSignature(\n\t\trawBody: string,\n\t\tsignature: string,\n\t\ttimestamp: string,\n\t\toptions?: { toleranceMs?: number },\n\t): boolean {\n\t\ttry {\n\t\t\tconst toleranceMs = options?.toleranceMs ?? 5 * 60 * 1000\n\t\t\tconst timestampMs = Number.parseInt(timestamp, 10)\n\t\t\tif (\n\t\t\t\tNumber.isNaN(timestampMs) ||\n\t\t\t\tMath.abs(Date.now() - timestampMs) > toleranceMs\n\t\t\t) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tconst rawString = `${timestamp}.${rawBody}`\n\t\t\tconst expected = crypto\n\t\t\t\t.createHmac(\"sha256\", this.appSecret)\n\t\t\t\t.update(rawString)\n\t\t\t\t.digest(\"hex\")\n\n\t\t\tconst signatureBuffer = Buffer.from(signature)\n\t\t\tconst expectedBuffer = Buffer.from(expected)\n\t\t\tif (signatureBuffer.length !== expectedBuffer.length) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn crypto.timingSafeEqual(signatureBuffer, expectedBuffer)\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Fetch products for the configured app.\n\t */\n\tasync getProducts(options?: {\n\t\tlocale?: string\n\t\tcurrency?: string\n\t}): Promise<Product[]> {\n\t\tconst params = new URLSearchParams()\n\t\tif (options?.locale) params.append(\"locale\", options.locale)\n\t\tif (options?.currency) params.append(\"currency\", options.currency)\n\n\t\tconst path = params.toString()\n\t\t\t? `/products?${params.toString()}`\n\t\t\t: \"/products\"\n\t\treturn this.request(\"GET\", path)\n\t}\n\n\t/**\n\t * Fetch the realtime stock snapshot for a single product.\n\t */\n\tasync getProductStock(\n\t\tproductIdOrCode: string,\n\t\toptions?: ProductStockQueryOptions,\n\t): Promise<ProductStock> {\n\t\tconst value = productIdOrCode?.trim()\n\t\tif (!value) {\n\t\t\tthrow new Error(\"productIdOrCode is required\")\n\t\t}\n\n\t\tconst params = new URLSearchParams()\n\t\tparams.set(\"productIdOrCode\", value)\n\t\tif (options?.lookupBy) params.set(\"lookupBy\", options.lookupBy)\n\t\tif (options?.locale) params.set(\"locale\", options.locale)\n\t\tif (options?.currency) params.set(\"currency\", options.currency)\n\n\t\treturn this.request(\n\t\t\t\"GET\",\n\t\t\t`/products/${encodeURIComponent(value)}/stock?${params.toString()}`,\n\t\t)\n\t}\n\n\t/**\n\t * Fetch realtime stock snapshots for multiple products.\n\t */\n\tasync getProductStocks(\n\t\tparams: ProductStocksQueryParams,\n\t\toptions?: ProductStockQueryOptions,\n\t): Promise<ProductStock[]>\n\tasync getProductStocks(\n\t\tparams: ProductStocksQueryParams & ProductStockQueryOptions,\n\t): Promise<ProductStock[]>\n\tasync getProductStocks(\n\t\tparams: ProductStocksQueryParams & ProductStockQueryOptions,\n\t\toptions?: ProductStockQueryOptions,\n\t): Promise<ProductStock[]> {\n\t\tconst productIds = [\n\t\t\t...new Set(\n\t\t\t\t(params.productIds || []).map((item) => item.trim()).filter(Boolean),\n\t\t\t),\n\t\t]\n\t\tconst productCodes = [\n\t\t\t...new Set(\n\t\t\t\t(params.productCodes || []).map((item) => item.trim()).filter(Boolean),\n\t\t\t),\n\t\t]\n\t\tif (productIds.length === 0 && productCodes.length === 0) {\n\t\t\tthrow new Error(\"productIds or productCodes is required\")\n\t\t}\n\n\t\tconst queryOptions = options || params\n\t\tconst query = new URLSearchParams()\n\t\tif (productIds.length > 0) query.set(\"productIds\", productIds.join(\",\"))\n\t\tif (productCodes.length > 0)\n\t\t\tquery.set(\"productCodes\", productCodes.join(\",\"))\n\t\tif (queryOptions.lookupBy) query.set(\"lookupBy\", queryOptions.lookupBy)\n\t\tif (queryOptions.locale) query.set(\"locale\", queryOptions.locale)\n\t\tif (queryOptions.currency) query.set(\"currency\", queryOptions.currency)\n\n\t\treturn this.request(\"GET\", `/products/stocks?${query.toString()}`)\n\t}\n\n\t/**\n\t * Create a new order\n\t * @param params - Order creation parameters\n\t * @returns Order details with payment parameters\n\t */\n\tasync createOrder(params: CreateOrderParams): Promise<CreateOrderResponse> {\n\t\treturn this.request(\"POST\", \"/orders\", params)\n\t}\n\n\t/**\n\t * Create a paid manual bank transfer order.\n\t * @param params - Order creation parameters without channel\n\t * @returns Paid order details\n\t */\n\tasync createBankTransferOrder(\n\t\tparams: CreateBankTransferOrderParams,\n\t): Promise<CreateOrderResponse> {\n\t\treturn this.request(\"POST\", \"/orders\", {\n\t\t\t...params,\n\t\t\tchannel: \"BANK_TRANSFER\",\n\t\t} satisfies CreateOrderParams)\n\t}\n\n\t/**\n\t * Create a WeChat Mini Program order (channel fixed to WECHAT_MINI)\n\t * @param params - Mini program order parameters\n\t * @returns Order details with payment parameters\n\t */\n\tasync createMiniProgramOrder(\n\t\tparams: CreateMiniProgramOrderParams,\n\t): Promise<CreateOrderResponse> {\n\t\tconst { openid, ...rest } = params\n\t\treturn this.request(\"POST\", \"/orders\", {\n\t\t\t...rest,\n\t\t\tchannel: \"WECHAT_MINI\",\n\t\t\topenid,\n\t\t\tmetadata: { openid },\n\t\t} satisfies CreateOrderParams)\n\t}\n\n\t/**\n\t * Pay for an existing order\n\t * @param orderId - The order ID to pay\n\t * @param params - Payment parameters including channel\n\t */\n\tasync payOrder(\n\t\torderId: string,\n\t\tparams: {\n\t\t\tchannel: string\n\t\t\treturnUrl?: string\n\t\t\topenid?: string\n\t\t\t[key: string]: any\n\t\t},\n\t): Promise<CreateOrderResponse> {\n\t\treturn this.request(\"POST\", `/orders/${orderId}/pay`, params)\n\t}\n\n\t/**\n\t * Cancel a pending order and release its inventory reservation.\n\t * Paid/refunded/failed orders are returned unchanged by the gateway.\n\t */\n\tasync cancelOrder(\n\t\torderId: string,\n\t\tparams?: { reason?: string },\n\t): Promise<CancelOrderResponse> {\n\t\tconst value = orderId?.trim()\n\t\tif (!value) {\n\t\t\tthrow new Error(\"orderId is required\")\n\t\t}\n\n\t\treturn this.request(\n\t\t\t\"POST\",\n\t\t\t`/orders/${encodeURIComponent(value)}/cancel`,\n\t\t\tparams || {},\n\t\t)\n\t}\n\n\t/**\n\t * Complete a zero-amount FREE order.\n\t * This marks the pending order as paid and triggers normal paid-order side effects.\n\t */\n\tasync completeFreeOrder(orderId: string): Promise<CompleteFreeOrderResponse> {\n\t\tconst value = orderId?.trim()\n\t\tif (!value) {\n\t\t\tthrow new Error(\"orderId is required\")\n\t\t}\n\n\t\treturn this.request(\n\t\t\t\"POST\",\n\t\t\t`/orders/${encodeURIComponent(value)}/free/complete`,\n\t\t\t{},\n\t\t)\n\t}\n\n\t/**\n\t * Query order status\n\t * @param orderId - The order ID to query\n\t */\n\tasync getOrderStatus(orderId: string): Promise<OrderStatus> {\n\t\treturn this.request(\"GET\", `/orders/${orderId}`)\n\t}\n\n\t/**\n\t * Get order details (full order information)\n\t * @param orderId - The order ID to query\n\t */\n\tasync getOrderDetails(orderId: string): Promise<OrderDetails> {\n\t\treturn this.request(\"GET\", `/orders/${orderId}/details`)\n\t}\n\n\t/**\n\t * Get orders list with pagination\n\t * @param params - Query parameters (pagination, filters)\n\t * @returns Orders list and pagination info\n\t */\n\tasync getOrders(params?: GetOrdersParams): Promise<GetOrdersResponse> {\n\t\tconst queryParams = new URLSearchParams()\n\t\tif (params?.page) queryParams.append(\"page\", params.page.toString())\n\t\tif (params?.pageSize)\n\t\t\tqueryParams.append(\"pageSize\", params.pageSize.toString())\n\t\tif (params?.userId) queryParams.append(\"userId\", params.userId)\n\t\tif (params?.status) queryParams.append(\"status\", params.status)\n\t\tif (params?.startDate) queryParams.append(\"startDate\", params.startDate)\n\t\tif (params?.endDate) queryParams.append(\"endDate\", params.endDate)\n\n\t\tconst path = queryParams.toString()\n\t\t\t? `/orders?${queryParams.toString()}`\n\t\t\t: \"/orders\"\n\t\treturn this.request<GetOrdersResponse>(\"GET\", path)\n\t}\n\n\t/**\n\t * Get user entitlements in the legacy flat shape.\n\t * @param userId - User ID\n\t */\n\tasync getEntitlements(userId: string): Promise<Record<string, any>> {\n\t\treturn this.request(\"GET\", `/users/${userId}/entitlements`)\n\t}\n\n\t/**\n\t * Get user entitlements with full details (type, expiry, reset config, source)\n\t * @param userId - User ID\n\t */\n\tasync getEntitlementsDetail(userId: string): Promise<EntitlementDetail> {\n\t\treturn this.request(\"GET\", `/users/${userId}/entitlements/detail`)\n\t}\n\n\tasync getActiveSubscription(\n\t\tuserId: string,\n\t): Promise<ActiveSubscriptionInfo | null> {\n\t\treturn this.request(\"GET\", `/users/${userId}/active-subscription`)\n\t}\n\n\t/**\n\t * Ensure user exists and auto-assign trial product if new user\n\t * This should be called when user first logs in or registers\n\t * @param userId - User ID\n\t */\n\tasync ensureUserWithTrial(\n\t\tuserId: string,\n\t): Promise<EnsureUserWithTrialResponse> {\n\t\treturn this.request(\"POST\", `/users/${userId}/entitlements/bootstrap`, {})\n\t}\n\n\t/**\n\t * Get a single entitlement value\n\t * @param userId - User ID\n\t * @param key - Entitlement key\n\t */\n\tasync getEntitlementValue(userId: string, key: string): Promise<any> {\n\t\tconst entitlements = await this.getEntitlements(userId)\n\t\treturn entitlements[key] ?? null\n\t}\n\n\t/**\n\t * Consume numeric entitlement\n\t * @param userId - User ID\n\t * @param key - Entitlement key\n\t * @param amount - Amount to consume\n\t */\n\tasync consumeEntitlement(\n\t\tuserId: string,\n\t\tkey: string,\n\t\tamount: number,\n\t\toptions?: {\n\t\t\tidempotencyKey?: string\n\t\t\tmetadata?: Record<string, any>\n\t\t},\n\t): Promise<{ balance: number }> {\n\t\treturn this.request(\"POST\", `/users/${userId}/entitlements/consume`, {\n\t\t\tkey,\n\t\t\tamount,\n\t\t\t...options,\n\t\t})\n\t}\n\n\t/**\n\t * Consume numeric entitlements from an ordered key pool.\n\t * Useful when subscription credits and perpetual credits use separate keys.\n\t */\n\tasync consumeEntitlementPool(\n\t\tuserId: string,\n\t\tkeys: string[],\n\t\tamount: number,\n\t\toptions?: {\n\t\t\tidempotencyKey?: string\n\t\t\tmetadata?: Record<string, any>\n\t\t},\n\t): Promise<ConsumeEntitlementPoolResult> {\n\t\treturn this.request(\"POST\", `/users/${userId}/entitlements/consume-pool`, {\n\t\t\tkeys,\n\t\t\tamount,\n\t\t\t...options,\n\t\t})\n\t}\n\n\t/**\n\t * Get active numeric entitlement buckets ordered by expiration.\n\t */\n\tasync getEntitlementCreditBuckets(\n\t\tuserId: string,\n\t\tkeys?: string[],\n\t): Promise<EntitlementCreditBucket[]> {\n\t\tconst query =\n\t\t\tkeys && keys.length > 0\n\t\t\t\t? `?keys=${encodeURIComponent(keys.join(\",\"))}`\n\t\t\t\t: \"\"\n\t\treturn this.request(\n\t\t\t\"GET\",\n\t\t\t`/users/${userId}/entitlements/credit-buckets${query}`,\n\t\t)\n\t}\n\n\t/**\n\t * Add numeric entitlement (e.g. refund)\n\t * @param userId - User ID\n\t * @param key - Entitlement key\n\t * @param amount - Amount to add\n\t */\n\tasync addEntitlement(\n\t\tuserId: string,\n\t\tkey: string,\n\t\tamount: number,\n\t): Promise<{ balance: number }> {\n\t\treturn this.request(\"POST\", `/users/${userId}/entitlements/add`, {\n\t\t\tkey,\n\t\t\tamount,\n\t\t})\n\t}\n\n\t/**\n\t * Toggle boolean entitlement\n\t * @param userId - User ID\n\t * @param key - Entitlement key\n\t * @param enabled - Whether to enable\n\t */\n\tasync toggleEntitlement(\n\t\tuserId: string,\n\t\tkey: string,\n\t\tenabled: boolean,\n\t): Promise<{ isEnabled: boolean }> {\n\t\t// Toggle endpoint expects POST with enabled flag\n\t\t// However, looking at list_dir, we have toggle/route.ts\n\t\t// I should verify its contract, but assuming standard toggle pattern:\n\t\treturn this.request(\"POST\", `/users/${userId}/entitlements/toggle`, {\n\t\t\tkey,\n\t\t\tenabled,\n\t\t})\n\t}\n\n\t/**\n\t * Generate checkout URL for client-side payment\n\t * @param productId - Product ID\n\t * @param priceId - Price ID\n\t * @returns Checkout page URL\n\t */\n\tgetCheckoutUrl(productId: string, priceId: string): string {\n\t\treturn `${this.checkoutUrl}/checkout/${this.appId}/${productId}/${priceId}`\n\t}\n\n\t/**\n\t * Verify a hosted login token and return the normalized login profile.\n\t * This request is signed with your app credentials and routed through the worker API.\n\t */\n\tasync verifyLoginToken(token: string): Promise<VerifiedLoginToken> {\n\t\tif (!token?.trim()) {\n\t\t\tthrow new Error(\"login token is required\")\n\t\t}\n\t\treturn this.request(\"POST\", \"/login/tokens/verify\", { token: token.trim() })\n\t}\n\n\t/**\n\t * Send a phone verification code for binding a phone number to a hosted login user.\n\t */\n\tasync sendPhoneVerificationCode(\n\t\tparams: SendPhoneVerificationCodeParams,\n\t): Promise<SendPhoneVerificationCodeResponse> {\n\t\tconst userId = params.userId?.trim()\n\t\tconst phoneNumber = params.phoneNumber?.trim()\n\t\tif (!userId) throw new Error(\"userId is required\")\n\t\tif (!phoneNumber) throw new Error(\"phoneNumber is required\")\n\n\t\treturn this.request(\n\t\t\t\"POST\",\n\t\t\t`/login/users/${encodeURIComponent(userId)}/phone/code`,\n\t\t\t{\n\t\t\t\tphoneCountryCode: params.phoneCountryCode || params.countryCode,\n\t\t\t\tphoneNumber,\n\t\t\t},\n\t\t)\n\t}\n\n\t/**\n\t * Bind a verified phone number to a hosted login user, merging existing accounts when needed.\n\t */\n\tasync bindPhoneNumber(\n\t\tparams: BindPhoneNumberParams,\n\t): Promise<BindPhoneNumberResponse> {\n\t\tconst userId = params.userId?.trim()\n\t\tconst phoneNumber = params.phoneNumber?.trim()\n\t\tconst code = params.code?.trim()\n\t\tif (!userId) throw new Error(\"userId is required\")\n\t\tif (!phoneNumber) throw new Error(\"phoneNumber is required\")\n\t\tif (!code) throw new Error(\"code is required\")\n\n\t\treturn this.request(\n\t\t\t\"POST\",\n\t\t\t`/login/users/${encodeURIComponent(userId)}/phone/bind`,\n\t\t\t{\n\t\t\t\tcode,\n\t\t\t\tphoneCountryCode: params.phoneCountryCode || params.countryCode,\n\t\t\t\tphoneNumber,\n\t\t\t},\n\t\t)\n\t}\n\n\t/**\n\t * Create a hosted WeChat official account binding URL for a user.\n\t */\n\tasync createWechatMessageBinding(\n\t\tparams: CreateWechatMessageBindingParams,\n\t): Promise<CreateWechatMessageBindingResponse> {\n\t\tconst loginExternalUserId =\n\t\t\tparams.loginExternalUserId?.trim() || params.userId?.trim()\n\t\tconst merchantUserId = params.merchantUserId?.trim()\n\t\tif (!loginExternalUserId && !merchantUserId) {\n\t\t\tthrow new Error(\"userId or merchantUserId is required\")\n\t\t}\n\n\t\treturn this.request(\"POST\", \"/messages/wechat/bindings\", {\n\t\t\t...params,\n\t\t\tloginExternalUserId,\n\t\t\tmerchantUserId,\n\t\t})\n\t}\n\n\t/**\n\t * Send an app-linked message template to a bound recipient.\n\t */\n\tasync sendMessage(params: SendMessageParams): Promise<SendMessageResponse> {\n\t\tif (!params.templateId?.trim() && !params.templateCode?.trim()) {\n\t\t\tthrow new Error(\"templateId or templateCode is required\")\n\t\t}\n\t\tif (!params.data || typeof params.data !== \"object\") {\n\t\t\tthrow new Error(\"data is required\")\n\t\t}\n\n\t\treturn this.request(\"POST\", \"/messages/send\", params)\n\t}\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@youidian/sdk",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.2",
|
|
4
4
|
"description": "Youidian Unified SDK - payment and hosted login SDK",
|
|
5
5
|
"author": "Youidian",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
"import": "./dist/client.js",
|
|
20
20
|
"require": "./dist/client.cjs"
|
|
21
21
|
},
|
|
22
|
+
"./feedback": {
|
|
23
|
+
"types": "./dist/feedback.d.ts",
|
|
24
|
+
"import": "./dist/feedback.js",
|
|
25
|
+
"require": "./dist/feedback.cjs"
|
|
26
|
+
},
|
|
22
27
|
"./server": {
|
|
23
28
|
"types": "./dist/server.d.ts",
|
|
24
29
|
"import": "./dist/server.js",
|