@cogito.ai/cli 0.4.3 → 0.4.5

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.
Files changed (56) hide show
  1. package/dist/index.js +1 -1
  2. package/dist/templates/web-nextjs/apps/docs/.source/browser.ts +8 -18
  3. package/dist/templates/web-nextjs/apps/docs/.source/dynamic.ts +5 -11
  4. package/dist/templates/web-nextjs/apps/docs/.source/server.ts +26 -37
  5. package/dist/templates/web-nextjs/apps/docs/content/docs/meta.json +1 -1
  6. package/dist/templates/web-nextjs/apps/docs/content/docs/template/alipay.mdx +276 -0
  7. package/dist/templates/web-nextjs/apps/docs/content/docs/template/deployment.mdx +32 -0
  8. package/dist/templates/web-nextjs/apps/docs/content/docs/template/drizzle.mdx +18 -0
  9. package/dist/templates/web-nextjs/apps/docs/content/docs/template/getting-started.mdx +98 -0
  10. package/dist/templates/web-nextjs/apps/docs/content/docs/template/meta.json +13 -0
  11. package/dist/templates/web-nextjs/apps/docs/content/docs/template/stripe.mdx +18 -0
  12. package/dist/templates/web-nextjs/apps/docs/content/docs/template/supabase.mdx +130 -0
  13. package/dist/templates/web-nextjs/apps/docs/content/docs/template/troubleshooting.mdx +87 -0
  14. package/dist/templates/web-nextjs/apps/docs/content/docs/template/wechat-pay.mdx +318 -0
  15. package/dist/templates/web-nextjs/apps/web/.env.example +35 -0
  16. package/dist/templates/web-nextjs/apps/web/messages/en.json +70 -0
  17. package/dist/templates/web-nextjs/apps/web/messages/zh.json +71 -1
  18. package/dist/templates/web-nextjs/apps/web/next-env.d.ts +1 -1
  19. package/dist/templates/web-nextjs/apps/web/package.json +4 -0
  20. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(auth)/forgot-password/page.tsx +4 -10
  21. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(protected)/payment/[paymentId]/page.tsx +88 -0
  22. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(protected)/payment/checkout/page.tsx +170 -0
  23. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(protected)/pricing/page.tsx +120 -0
  24. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(protected)/settings/layout.tsx +48 -1
  25. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(protected)/settings/subscription/page.tsx +128 -0
  26. package/dist/templates/web-nextjs/apps/web/src/app/api/payments/alipay/create/route.ts +43 -0
  27. package/dist/templates/web-nextjs/apps/web/src/app/api/payments/alipay/notify/route.ts +105 -0
  28. package/dist/templates/web-nextjs/apps/web/src/app/api/payments/alipay/query/route.ts +54 -0
  29. package/dist/templates/web-nextjs/apps/web/src/app/api/payments/alipay/return/route.ts +20 -0
  30. package/dist/templates/web-nextjs/apps/web/src/app/api/payments/create/route.ts +52 -0
  31. package/dist/templates/web-nextjs/apps/web/src/app/api/payments/wechat/create/route.ts +58 -0
  32. package/dist/templates/web-nextjs/apps/web/src/app/api/payments/wechat/notify/route.ts +92 -0
  33. package/dist/templates/web-nextjs/apps/web/src/app/api/payments/wechat/query/route.ts +54 -0
  34. package/dist/templates/web-nextjs/apps/web/src/app/docs/page.tsx +5 -0
  35. package/dist/templates/web-nextjs/apps/web/src/features/subscription/alipay/client.ts +36 -0
  36. package/dist/templates/web-nextjs/apps/web/src/features/subscription/alipay/server.ts +83 -0
  37. package/dist/templates/web-nextjs/apps/web/src/features/subscription/components/index.ts +4 -0
  38. package/dist/templates/web-nextjs/apps/web/src/features/subscription/components/pro-badge.tsx +9 -0
  39. package/dist/templates/web-nextjs/apps/web/src/features/subscription/components/pro-feature-comparison.tsx +70 -0
  40. package/dist/templates/web-nextjs/apps/web/src/features/subscription/components/quota-warning-banner.tsx +37 -0
  41. package/dist/templates/web-nextjs/apps/web/src/features/subscription/components/upgrade-button.tsx +42 -0
  42. package/dist/templates/web-nextjs/apps/web/src/features/subscription/hooks.ts +141 -0
  43. package/dist/templates/web-nextjs/apps/web/src/features/subscription/payment-helpers.ts +27 -0
  44. package/dist/templates/web-nextjs/apps/web/src/features/subscription/payment-service.ts +56 -0
  45. package/dist/templates/web-nextjs/apps/web/src/features/subscription/service.ts +55 -0
  46. package/dist/templates/web-nextjs/apps/web/src/features/subscription/types.ts +73 -0
  47. package/dist/templates/web-nextjs/apps/web/src/features/subscription/wechat/client.ts +33 -0
  48. package/dist/templates/web-nextjs/apps/web/src/features/subscription/wechat/server.ts +147 -0
  49. package/dist/templates/web-nextjs/apps/web/src/lib/supabase/admin.ts +23 -0
  50. package/dist/templates/web-nextjs/apps/web/src/lib/wechat-pay/client.ts +66 -0
  51. package/dist/templates/web-nextjs/apps/web/src/lib/wechat-pay/crypto.ts +99 -0
  52. package/dist/templates/web-nextjs/apps/web/src/lib/wechat-pay/types.ts +10 -0
  53. package/dist/templates/web-nextjs/pnpm-lock.yaml +319 -0
  54. package/dist/templates/web-nextjs/pnpm-workspace.yaml +7 -8
  55. package/dist/templates/web-nextjs/supabase/migrations/20250608_add_subscription_tables.sql +125 -0
  56. package/package.json +1 -1
@@ -0,0 +1,147 @@
1
+ import { wxpayRequest } from '@/lib/wechat-pay/client'
2
+ import type { WechatPayConfig } from '@/lib/wechat-pay/types'
3
+ import { decryptResource, verifySignature } from '@/lib/wechat-pay/crypto'
4
+
5
+ export function getWechatPayConfig(): WechatPayConfig {
6
+ const appId = process.env.WECHAT_PAY_APP_ID
7
+ const mchId = process.env.WECHAT_PAY_MCH_ID
8
+ const privateKey = process.env.WECHAT_PAY_PRIVATE_KEY
9
+ const serialNo = process.env.WECHAT_PAY_SERIAL_NO
10
+ const apiV3Key = process.env.WECHAT_PAY_API_V3_KEY
11
+ const platformPublicKey = process.env.WECHAT_PAY_PLATFORM_PUBLIC_KEY
12
+ const baseUrl = process.env.WECHAT_PAY_BASE_URL || 'https://api.mch.weixin.qq.com'
13
+ const notifyUrl = process.env.WECHAT_PAY_NOTIFY_URL
14
+
15
+ if (
16
+ !appId ||
17
+ !mchId ||
18
+ !privateKey ||
19
+ !serialNo ||
20
+ !apiV3Key ||
21
+ !platformPublicKey ||
22
+ !notifyUrl
23
+ ) {
24
+ throw new Error(
25
+ 'Missing WeChat Pay credentials. Ensure all WECHAT_PAY_* environment variables are set.',
26
+ )
27
+ }
28
+
29
+ return {
30
+ appId,
31
+ mchId,
32
+ privateKey,
33
+ serialNo,
34
+ apiV3Key,
35
+ platformPublicKey,
36
+ baseUrl,
37
+ notifyUrl,
38
+ }
39
+ }
40
+
41
+ interface CreateWechatPayParams {
42
+ description: string
43
+ outTradeNo: string
44
+ notifyUrl: string
45
+ amount: { total: number; currency?: string }
46
+ sceneInfo?: object
47
+ }
48
+
49
+ export async function createWechatNativePay(params: CreateWechatPayParams) {
50
+ const config = getWechatPayConfig()
51
+ const body = {
52
+ appid: config.appId,
53
+ mchid: config.mchId,
54
+ description: params.description,
55
+ out_trade_no: params.outTradeNo,
56
+ notify_url: params.notifyUrl,
57
+ amount: params.amount,
58
+ }
59
+
60
+ return wxpayRequest<{ code_url: string }>({
61
+ config,
62
+ method: 'post',
63
+ url: '/v3/pay/transactions/native',
64
+ body,
65
+ })
66
+ }
67
+
68
+ export async function createWechatH5Pay(params: CreateWechatPayParams) {
69
+ const config = getWechatPayConfig()
70
+ const body = {
71
+ appid: config.appId,
72
+ mchid: config.mchId,
73
+ description: params.description,
74
+ out_trade_no: params.outTradeNo,
75
+ notify_url: params.notifyUrl,
76
+ amount: params.amount,
77
+ scene_info: params.sceneInfo,
78
+ }
79
+
80
+ return wxpayRequest<{ h5_url: string }>({
81
+ config,
82
+ method: 'post',
83
+ url: '/v3/pay/transactions/h5',
84
+ body,
85
+ })
86
+ }
87
+
88
+ export async function queryWechatOrder(outTradeNo: string) {
89
+ const config = getWechatPayConfig()
90
+ return wxpayRequest<{ trade_state: string }>({
91
+ config,
92
+ method: 'get',
93
+ url: `/v3/pay/transactions/out-trade-no/${outTradeNo}`,
94
+ })
95
+ }
96
+
97
+ export interface VerifyAndDecryptNotifyResult {
98
+ outTradeNo: string
99
+ tradeState: string
100
+ transactionId: string
101
+ amount: number
102
+ currency: string
103
+ appId: string
104
+ mchId: string
105
+ }
106
+
107
+ export async function verifyAndDecryptNotify(
108
+ headers: Record<string, string | undefined>,
109
+ rawBody: string,
110
+ ): Promise<VerifyAndDecryptNotifyResult> {
111
+ const config = getWechatPayConfig()
112
+ const timestamp = headers['wechatpay-timestamp'] || headers['Wechatpay-Timestamp'] || ''
113
+ const nonce = headers['wechatpay-nonce'] || headers['Wechatpay-Nonce'] || ''
114
+ const signature = headers['wechatpay-signature'] || headers['Wechatpay-Signature'] || ''
115
+
116
+ const isValid = verifySignature({
117
+ timestamp,
118
+ nonce,
119
+ body: rawBody,
120
+ signature,
121
+ platformPublicKey: config.platformPublicKey,
122
+ })
123
+
124
+ if (!isValid) {
125
+ throw new Error('Invalid WeChat Pay signature')
126
+ }
127
+
128
+ const bodyData = JSON.parse(rawBody)
129
+ const resource = bodyData.resource
130
+ const decrypted = decryptResource({
131
+ ciphertext: resource.ciphertext,
132
+ associatedData: resource.associated_data,
133
+ nonce: resource.nonce,
134
+ apiV3Key: config.apiV3Key,
135
+ })
136
+
137
+ const transaction = JSON.parse(decrypted)
138
+ return {
139
+ outTradeNo: transaction.out_trade_no,
140
+ tradeState: transaction.trade_state,
141
+ transactionId: transaction.transaction_id,
142
+ amount: transaction.amount?.total,
143
+ currency: transaction.amount?.currency,
144
+ appId: transaction.appid,
145
+ mchId: transaction.mchid,
146
+ }
147
+ }
@@ -0,0 +1,23 @@
1
+ import { createClient } from '@supabase/supabase-js'
2
+
3
+ /**
4
+ * Creates a Supabase admin client using the service role key.
5
+ *
6
+ * ⚠️ WARNING: This client bypasses Row Level Security (RLS).
7
+ * Use ONLY in server-side contexts (Route Handlers, Server Actions).
8
+ * NEVER expose this client to the browser.
9
+ */
10
+ export function createAdminClient() {
11
+ const url = process.env.NEXT_PUBLIC_SUPABASE_URL
12
+ const key = process.env.SUPABASE_SERVICE_ROLE_KEY
13
+
14
+ if (!url || !key) {
15
+ throw new Error(
16
+ 'Missing Supabase admin credentials. Ensure NEXT_PUBLIC_SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY are set.',
17
+ )
18
+ }
19
+
20
+ return createClient(url, key, {
21
+ auth: { autoRefreshToken: false, persistSession: false },
22
+ })
23
+ }
@@ -0,0 +1,66 @@
1
+ import { buildAuthorizationHeader, generateNonce, signRequest } from './crypto'
2
+ import type { WechatPayConfig } from './types'
3
+
4
+ interface WxpayRequestParams<T = unknown> {
5
+ config: WechatPayConfig
6
+ method: string
7
+ url: string
8
+ body?: object
9
+ }
10
+
11
+ export async function wxpayRequest<T>(params: WxpayRequestParams): Promise<T> {
12
+ const { config, method, url, body } = params
13
+ const timestamp = Math.floor(Date.now() / 1000).toString()
14
+ const nonce = generateNonce()
15
+ const bodyStr = body ? JSON.stringify(body) : ''
16
+
17
+ const signature = signRequest({
18
+ method,
19
+ url,
20
+ timestamp,
21
+ nonce,
22
+ body: bodyStr,
23
+ privateKey: config.privateKey,
24
+ })
25
+
26
+ const authorization = buildAuthorizationHeader({
27
+ mchId: config.mchId,
28
+ serialNo: config.serialNo,
29
+ timestamp,
30
+ nonce,
31
+ signature,
32
+ })
33
+
34
+ const headers: Record<string, string> = {
35
+ Accept: 'application/json',
36
+ Authorization: authorization,
37
+ 'Content-Type': 'application/json',
38
+ 'User-Agent': 'Mozilla/5.0 (compatible; WebNextjs/1.0)',
39
+ }
40
+
41
+ const res = await fetch(`${config.baseUrl}${url}`, {
42
+ method: method.toUpperCase(),
43
+ headers,
44
+ body: bodyStr || null,
45
+ })
46
+
47
+ const data = (await res.json()) as T
48
+
49
+ // Check for WeChat Pay API errors
50
+ if (!res.ok) {
51
+ const errorData = data as Record<string, unknown>
52
+ const errorMessage =
53
+ typeof errorData.message === 'string'
54
+ ? errorData.message
55
+ : `WeChat Pay API error: ${res.status}`
56
+ const error = new Error(errorMessage)
57
+ ;(error as Error & { code?: string; status?: number; raw?: unknown }).code = String(
58
+ errorData.code || '',
59
+ )
60
+ ;(error as Error & { code?: string; status?: number; raw?: unknown }).status = res.status
61
+ ;(error as Error & { code?: string; status?: number; raw?: unknown }).raw = errorData
62
+ throw error
63
+ }
64
+
65
+ return data
66
+ }
@@ -0,0 +1,99 @@
1
+ import crypto from 'crypto'
2
+
3
+ /**
4
+ * Generates a random nonce string (16 hex chars, uppercase)
5
+ */
6
+ export function generateNonce(): string {
7
+ return crypto.randomBytes(16).toString('hex').toUpperCase()
8
+ }
9
+
10
+ interface SignRequestParams {
11
+ method: string
12
+ url: string
13
+ timestamp: string
14
+ nonce: string
15
+ body: string
16
+ privateKey: string
17
+ }
18
+
19
+ /**
20
+ * Constructs the -line signature string and signs it with SHA256withRSA using the merchant private key.
21
+ * Returns Base64 encoded signature.
22
+ */
23
+ export function signRequest(params: SignRequestParams): string {
24
+ const { method, url, timestamp, nonce, body, privateKey } = params
25
+ const signStr = `${method.toUpperCase()}\n${url}\n${timestamp}\n${nonce}\n${body}\n`
26
+
27
+ const signer = crypto.createSign('RSA-SHA256')
28
+ signer.update(signStr)
29
+ return signer.sign(privateKey, 'base64')
30
+ }
31
+
32
+ interface BuildAuthorizationHeaderParams {
33
+ mchId: string
34
+ serialNo: string
35
+ timestamp: string
36
+ nonce: string
37
+ signature: string
38
+ }
39
+
40
+ /**
41
+ * Builds the Authorization header for WeChat Pay V3 API requests.
42
+ */
43
+ export function buildAuthorizationHeader(params: BuildAuthorizationHeaderParams): string {
44
+ const { mchId, serialNo, timestamp, nonce, signature } = params
45
+ return `WECHATPAY2-SHA256-RSA2048 mchid="${mchId}",nonce_str="${nonce}",timestamp="${timestamp}",serial_no="${serialNo}",signature="${signature}"`
46
+ }
47
+
48
+ interface VerifySignatureParams {
49
+ timestamp: string
50
+ nonce: string
51
+ body: string
52
+ signature: string
53
+ platformPublicKey: string
54
+ }
55
+
56
+ /**
57
+ * Verifies WeChat Pay notification signature using the platform public key.
58
+ */
59
+ export function verifySignature(params: VerifySignatureParams): boolean {
60
+ const { timestamp, nonce, body, signature, platformPublicKey } = params
61
+ const verifyStr = `${timestamp}\n${nonce}\n${body}\n`
62
+
63
+ const verifier = crypto.createVerify('RSA-SHA256')
64
+ verifier.update(verifyStr)
65
+ return verifier.verify(platformPublicKey, signature, 'base64')
66
+ }
67
+
68
+ interface DecryptResourceParams {
69
+ ciphertext: string
70
+ associatedData: string
71
+ nonce: string
72
+ apiV3Key: string
73
+ }
74
+
75
+ /**
76
+ * Decrypts AEAD_AES_256_GCM encrypted resource.
77
+ * The ciphertext includes the auth tag as the last 16 bytes.
78
+ */
79
+ export function decryptResource(params: DecryptResourceParams): string {
80
+ const { ciphertext, associatedData, nonce, apiV3Key } = params
81
+ const buf = Buffer.from(ciphertext, 'base64')
82
+
83
+ // AES-256-GCM: last 16 bytes are the auth tag
84
+ const authTagLength = 16
85
+ const encrypted = buf.slice(0, -authTagLength)
86
+ const authTag = buf.slice(-authTagLength)
87
+
88
+ const decipher = crypto.createDecipheriv(
89
+ 'aes-256-gcm',
90
+ Buffer.from(apiV3Key),
91
+ Buffer.from(nonce, 'utf8'),
92
+ )
93
+ decipher.setAuthTag(authTag)
94
+ decipher.setAAD(Buffer.from(associatedData))
95
+
96
+ let decrypted = decipher.update(encrypted, undefined, 'utf8')
97
+ decrypted += decipher.final('utf8')
98
+ return decrypted
99
+ }
@@ -0,0 +1,10 @@
1
+ export interface WechatPayConfig {
2
+ appId: string
3
+ mchId: string
4
+ privateKey: string // PKCS8 PEM format merchant private key
5
+ serialNo: string // Merchant certificate serial number
6
+ apiV3Key: string // 32-byte APIv3 key
7
+ platformPublicKey: string // WeChat platform public key PEM (downloaded from /v3/certificates)
8
+ baseUrl: string // Domestic: https://api.mch.weixin.qq.com; Overseas: https://apihk.mch.weixin.qq.com
9
+ notifyUrl: string
10
+ }