diff --git a/src/controller/webhook.controller.ts b/src/controller/webhook.controller.ts index 69a070d..bf45f32 100644 --- a/src/controller/webhook.controller.ts +++ b/src/controller/webhook.controller.ts @@ -13,9 +13,13 @@ import * as crypto from 'crypto'; import { SiteService } from '../service/site.service'; import { OrderService } from '../service/order.service'; +import { + UnifiedOrderDTO, +} from '../dto/site-api.dto'; + @Controller('/webhook') export class WebhookController { - private secret = 'YOONE24kd$kjcdjflddd'; + private secret = '$kjYOONE24kdcdjflddd'; // 平台服务保留按需注入 @@ -116,4 +120,88 @@ export class WebhookController { console.log(error); } } + + + + @Post('/shoppy') + async handleShoppyWebhook( + @Body() body: any, + @Query('siteId') siteIdStr: string, + @Query('signature') signature: string, + @Headers() header: any + ) { + const topic = header['x-oemsaas-event-type']; + // const source = header['x-oemsaas-shop-domain']; + const siteId = Number(siteIdStr); + const bodys = new UnifiedOrderDTO(); + Object.assign(bodys, body); + // 从数据库获取站点配置 + const site = await this.siteService.get(siteId, true); + + // if (!site || !source?.includes(site.websiteUrl)) { + if (!site) { + console.log('domain not match'); + return { + code: HttpStatus.BAD_REQUEST, + success: false, + message: 'domain not match', + }; + } + + if (!signature) { + return { + code: HttpStatus.BAD_REQUEST, + success: false, + message: 'Signature missing', + }; + } + + //shopyy 无法提供加密字段校验,注释校验逻辑 + // const rawBody = this.ctx.request.rawBody; + // const hash = crypto + // .createHmac('sha256', this.secret) + // .update(rawBody) + // .digest('base64'); + try { + if (this.secret === signature) { + switch (topic) { + case 'product.created': + case 'product.updated': + // 不再写入本地,平台事件仅确认接收 + break; + case 'product.deleted': + // 不再写入本地,平台事件仅确认接收 + break; + case 'orders/create': + case 'orders/update': + await this.orderService.syncSingleOrder(siteId, bodys); + break; + case 'orders/delete': + break; + case 'customer.created': + break; + case 'customer.updated': + break; + case 'customer.deleted': + break; + default: + console.log('Unhandled event:', topic); + } + + return { + code: 200, + success: true, + message: 'Webhook processed successfully', + }; + } else { + return { + code: 403, + success: false, + message: 'Webhook verification failed', + }; + } + } catch (error) { + console.log(error); + } + } } diff --git a/src/entity/site.entity.ts b/src/entity/site.entity.ts index a579a33..08b10a5 100644 --- a/src/entity/site.entity.ts +++ b/src/entity/site.entity.ts @@ -13,6 +13,9 @@ export class Site { @Column({ name: 'website_url', length: 255, nullable: true }) websiteUrl: string; + @Column({ name: 'webhook_url', length: 255, nullable: true }) + webhookUrl: string; + @Column({ length: 255, nullable: true }) consumerKey?: string; diff --git a/src/middleware/auth.middleware.ts b/src/middleware/auth.middleware.ts index 69a1495..e0fd0cf 100644 --- a/src/middleware/auth.middleware.ts +++ b/src/middleware/auth.middleware.ts @@ -21,6 +21,7 @@ export class AuthMiddleware implements IMiddleware { whiteList = [ '/user/login', '/webhook/woocommerce', + '/webhook/shoppy', '/logistics/getTrackingNumber', '/logistics/getListByTrackingId', '/product/categories/all',