194 lines
5.0 KiB
TypeScript
194 lines
5.0 KiB
TypeScript
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);
|
|
}
|
|
}
|
|
|
|
}
|