mirror of
https://github.com/saltbo/zpan.git
synced 2026-08-30 17:50:07 +08:00
fix(store): use unified delivery webhook route
This commit is contained in:
+1
-1
@@ -66,7 +66,7 @@ export function createApp(platform: Platform, auth: Auth) {
|
||||
app.route('/api/licensing', licensing)
|
||||
app.route('/api/branding', publicBranding)
|
||||
app.route('/api/site-invitations', publicSiteInvitations)
|
||||
app.route('/api/store/webhooks', cloudStoreWebhooks)
|
||||
app.route('/api/store', cloudStoreWebhooks)
|
||||
|
||||
app.use('/api/*', authMiddleware)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ const CLOUD_EVENT_TOKEN_MAX_TTL_SECONDS = 5 * 60
|
||||
|
||||
const cloudEventTokenSchema = z.object({
|
||||
type: z.literal('zpan.cloud.event'),
|
||||
purpose: z.enum(['quota_store.delivery', 'store.delivery']),
|
||||
purpose: z.literal('store.delivery'),
|
||||
issuer: z.string().min(1),
|
||||
audience: z.string().min(1),
|
||||
boundLicenseId: z.string().min(1),
|
||||
|
||||
@@ -1007,6 +1007,9 @@ describe('Quota Store API', () => {
|
||||
})
|
||||
|
||||
expect(checkout.status).toBe(200)
|
||||
expect(orderPayload()).toMatchObject({
|
||||
deliveryCallbackUrl: 'http://localhost/api/store/webhook',
|
||||
})
|
||||
expect(paymentPayload()).toMatchObject({
|
||||
successUrl: 'http://localhost/storage',
|
||||
cancelUrl: 'http://localhost/storage',
|
||||
@@ -1060,6 +1063,9 @@ describe('Quota Store API', () => {
|
||||
})
|
||||
|
||||
expect(checkout.status).toBe(200)
|
||||
expect(orderPayload()).toMatchObject({
|
||||
deliveryCallbackUrl: 'http://localhost/api/store/webhook',
|
||||
})
|
||||
expect(paymentPayload()).toMatchObject({
|
||||
successUrl: 'http://localhost/storage',
|
||||
cancelUrl: 'http://localhost/storage',
|
||||
@@ -1080,6 +1086,9 @@ describe('Quota Store API', () => {
|
||||
})
|
||||
|
||||
expect(checkout.status).toBe(200)
|
||||
expect(orderPayload()).toMatchObject({
|
||||
deliveryCallbackUrl: 'https://files.example.com/api/store/webhook',
|
||||
})
|
||||
expect(paymentPayload()).toMatchObject({
|
||||
successUrl: 'https://files.example.com/storage',
|
||||
cancelUrl: 'https://files.example.com/storage',
|
||||
@@ -1100,6 +1109,9 @@ describe('Quota Store API', () => {
|
||||
})
|
||||
|
||||
expect(checkout.status).toBe(200)
|
||||
expect(orderPayload()).toMatchObject({
|
||||
deliveryCallbackUrl: 'https://auth.example.com/api/store/webhook',
|
||||
})
|
||||
expect(paymentPayload()).toMatchObject({
|
||||
successUrl: 'https://auth.example.com/storage',
|
||||
cancelUrl: 'https://auth.example.com/storage',
|
||||
@@ -1120,6 +1132,9 @@ describe('Quota Store API', () => {
|
||||
})
|
||||
|
||||
expect(checkout.status).toBe(200)
|
||||
expect(orderPayload()).toMatchObject({
|
||||
deliveryCallbackUrl: 'http://localhost/api/store/webhook',
|
||||
})
|
||||
expect(paymentPayload()).toMatchObject({
|
||||
successUrl: 'http://localhost/storage',
|
||||
cancelUrl: 'http://localhost/storage',
|
||||
@@ -1140,6 +1155,9 @@ describe('Quota Store API', () => {
|
||||
})
|
||||
|
||||
expect(checkout.status).toBe(200)
|
||||
expect(orderPayload()).toMatchObject({
|
||||
deliveryCallbackUrl: 'http://localhost/api/store/webhook',
|
||||
})
|
||||
expect(paymentPayload()).toMatchObject({
|
||||
successUrl: 'http://localhost/storage',
|
||||
cancelUrl: 'http://localhost/storage',
|
||||
@@ -2044,7 +2062,7 @@ describe('Quota Store API', () => {
|
||||
expiresAt: '2026-06-01T00:00:00.000Z',
|
||||
})
|
||||
|
||||
const res = await app.request('/api/store/webhooks/cloud', {
|
||||
const res = await app.request('/api/store/webhook', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -2885,7 +2903,7 @@ describe('Quota Store API', () => {
|
||||
const headers = await adminHeaders(app)
|
||||
await seedSettings(app, headers)
|
||||
|
||||
const res = await app.request('/api/store/webhooks/cloud', {
|
||||
const res = await app.request('/api/store/webhook', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -2903,7 +2921,7 @@ describe('Quota Store API', () => {
|
||||
await seedSettings(app, headers)
|
||||
const payload = JSON.stringify({ eventId: 'evt-bad-token' })
|
||||
|
||||
const res = await app.request('/api/store/webhooks/cloud', {
|
||||
const res = await app.request('/api/store/webhook', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -2922,7 +2940,7 @@ describe('Quota Store API', () => {
|
||||
await seedSettings(app, headers)
|
||||
const payload = JSON.stringify({ eventId: 'evt-wrong-purpose' })
|
||||
|
||||
const res = await app.request('/api/store/webhooks/cloud', {
|
||||
const res = await app.request('/api/store/webhook', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -2942,7 +2960,7 @@ describe('Quota Store API', () => {
|
||||
const payload = JSON.stringify({ eventId: 'evt-expired-token' })
|
||||
const now = Math.floor(Date.now() / 1000)
|
||||
|
||||
const res = await app.request('/api/store/webhooks/cloud', {
|
||||
const res = await app.request('/api/store/webhook', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -2961,7 +2979,7 @@ describe('Quota Store API', () => {
|
||||
await seedSettings(app, headers)
|
||||
const payload = JSON.stringify({ eventId: 'evt-missing-issued-at' })
|
||||
|
||||
const res = await app.request('/api/store/webhooks/cloud', {
|
||||
const res = await app.request('/api/store/webhook', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -2981,7 +2999,7 @@ describe('Quota Store API', () => {
|
||||
const payload = JSON.stringify({ eventId: 'evt-future-issued-at' })
|
||||
const issuedAt = Math.floor(Date.now() / 1000) + 60
|
||||
|
||||
const res = await app.request('/api/store/webhooks/cloud', {
|
||||
const res = await app.request('/api/store/webhook', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -3001,7 +3019,7 @@ describe('Quota Store API', () => {
|
||||
const payload = JSON.stringify({ eventId: 'evt-overlong-token' })
|
||||
const issuedAt = Math.floor(Date.now() / 1000)
|
||||
|
||||
const res = await app.request('/api/store/webhooks/cloud', {
|
||||
const res = await app.request('/api/store/webhook', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -3020,7 +3038,7 @@ describe('Quota Store API', () => {
|
||||
await seedSettings(app, headers)
|
||||
const payload = JSON.stringify({ eventId: 'evt-wrong-hash' })
|
||||
|
||||
const res = await app.request('/api/store/webhooks/cloud', {
|
||||
const res = await app.request('/api/store/webhook', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -3048,7 +3066,7 @@ describe('Quota Store API', () => {
|
||||
source: 'stripe',
|
||||
})
|
||||
|
||||
const res = await app.request('/api/store/webhooks/cloud', {
|
||||
const res = await app.request('/api/store/webhook', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -3067,7 +3085,7 @@ describe('Quota Store API', () => {
|
||||
await seedSettings(app, headers)
|
||||
const payload = JSON.stringify({ eventId: 'evt-wrong-audience' })
|
||||
|
||||
const res = await app.request('/api/store/webhooks/cloud', {
|
||||
const res = await app.request('/api/store/webhook', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -3141,7 +3159,7 @@ async function seedSettingsRow(db: Awaited<ReturnType<typeof createTestApp>>['db
|
||||
}
|
||||
|
||||
async function postWebhook(app: Awaited<ReturnType<typeof createTestApp>>['app'], payload: string) {
|
||||
return app.request('/api/store/webhooks/cloud', {
|
||||
return app.request('/api/store/webhook', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -3156,7 +3174,7 @@ async function signedWebhookHeaders(payload: string, overrides: Record<string, u
|
||||
const issuedAt = Math.floor(Date.now() / 1000)
|
||||
const token = sign(EVENT_SECRET, {
|
||||
type: 'zpan.cloud.event',
|
||||
purpose: 'quota_store.delivery',
|
||||
purpose: 'store.delivery',
|
||||
issuer: ZPAN_CLOUD_URL_DEFAULT,
|
||||
audience: 'test-instance',
|
||||
boundLicenseId: 'test-binding',
|
||||
|
||||
@@ -112,6 +112,7 @@ export const cloudStore = new Hono<Env>()
|
||||
{
|
||||
items: [{ productId: body.packageId, priceId: price.id }],
|
||||
currency,
|
||||
deliveryCallbackUrl: `${getInstanceOrigin(c)}/api/store/webhook`,
|
||||
target: {
|
||||
orgId: targetOrgId,
|
||||
endUserId: targetOrgId,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { requireFeature } from '../../middleware/require-feature'
|
||||
import { getCloudStoreBinding, getRequiredSettings, processCloudOrderQuotaChange } from '../../services/cloud-store'
|
||||
import { getCloudBaseUrl, parseJson, sha256Hex } from '../cloud-store-helpers'
|
||||
|
||||
export const cloudStoreWebhooks = new Hono<Env>().use(requireFeature('quota_store')).post('/cloud', async (c) => {
|
||||
export const cloudStoreWebhooks = new Hono<Env>().use(requireFeature('quota_store')).post('/webhook', async (c) => {
|
||||
const db = c.get('platform').db
|
||||
await getRequiredSettings(db)
|
||||
const binding = await getCloudStoreBinding(db)
|
||||
|
||||
Reference in New Issue
Block a user