import { HttpStatus, ILogger, Inject, Logger } from '@midwayjs/core'; import { Controller, Post, Body, Headers, Get, Query, } from '@midwayjs/decorator'; import { Context } from '@midwayjs/koa'; import * as crypto from 'crypto'; import { SiteService } from '../service/site.service'; import { OrderService } from '../service/order.service'; import { SiteApiService } from '../service/site-api.service'; @Controller('/webhook') export class WebhookController { private secret = 'YOONE24kd$kjcdjflddd'; // 平台服务保留按需注入 @Inject() private readonly orderService: OrderService; @Inject() ctx: Context; @Logger() logger: ILogger; @Inject() private readonly siteService: SiteService; @Inject() private readonly siteApiService: SiteApiService; // 移除配置中的站点数组,来源统一改为数据库 @Get('/') async test() { return 'webhook'; } // TODO 这里得检查一下是否对 SHOPYY有效,否则得另外书写 @Post('/woocommerce') async handleWooWebhook( @Body() body: any, @Query('siteId') siteIdStr: string, @Headers() header: any ) { console.log(`webhook woocommerce`, siteIdStr, body, header) const signature = header['x-wc-webhook-signature']; const topic = header['x-wc-webhook-topic']; const source = header['x-wc-webhook-source']; const siteId = Number(siteIdStr); // 从数据库获取站点配置 const site = await this.siteService.get(siteId, true); if (!site || !source?.includes(site.apiUrl)) { 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', }; } const rawBody = this.ctx.request.rawBody; const hash = crypto .createHmac('sha256', this.secret) .update(rawBody) .digest('base64'); try { if (hash !== signature) { return { code: 403, success: false, message: 'Webhook verification failed', }; } const adapter = await this.siteApiService.getAdapter(siteId); switch (topic) { case 'product.created': case 'product.updated': // 不再写入本地,平台事件仅确认接收 break; case 'product.deleted': // 不再写入本地,平台事件仅确认接收 break; case 'order.created': case 'order.updated': const order = adapter.mapPlatformToUnifiedOrder(body) await this.orderService.syncSingleOrder(siteId, order); break; case 'order.deleted': break; case 'customer.created': break; case 'customer.updated': break; case 'customer.deleted': break; default: console.log('Unhandled event:', body.event); return { code: 200, success: true, message: 'Webhook processed successfully', }; } } catch (error) { console.log(error); } } @Post('/shoppy') async handleShoppyWebhook( @Body() body: any, @Query('siteId') siteIdStr: string, @Query('signature') signature: string, @Headers() header: any ) { console.log(`webhook shoppy`, siteIdStr, body, header) const topic = header['x-oemsaas-event-type']; // const source = header['x-oemsaas-shop-domain']; const siteId = Number(siteIdStr); 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'); const adapter = await this.siteApiService.getAdapter(siteId); try { if (this.secret === signature) { switch (topic) { case 'product.created': case 'product.updated': // 不再写入本地,平台事件仅确认接收 break; case 'product.deleted': // 不再写入本地,平台事件仅确认接收 break; case 'orders/create': case 'orders/update': const order = adapter.mapPlatformToUnifiedOrder(body) await this.orderService.syncSingleOrder(siteId, order); 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', }; } } catch (error) { console.log(error); } } }