max 5 сар өмнө
parent
commit
9e723dd362

+ 17 - 16
package.json

@@ -7,23 +7,24 @@
     "@cool-midway/core": "^7.1.20",
     "@cool-midway/rpc": "^7.0.0",
     "@cool-midway/task": "^7.0.0",
-    "@midwayjs/axios": "^3.19.1",
-    "@midwayjs/bootstrap": "^3.16.0",
-    "@midwayjs/cache-manager": "^3.16.0",
-    "@midwayjs/core": "^3.16.0",
-    "@midwayjs/cron": "^3.16.0",
-    "@midwayjs/cross-domain": "^3.16.1",
-    "@midwayjs/decorator": "^3.16.0",
-    "@midwayjs/info": "^3.16.1",
-    "@midwayjs/koa": "^3.16.1",
+    "@midwayjs/axios": "^3.19.2",
+    "@midwayjs/bootstrap": "^3.19.3",
+    "@midwayjs/cache-manager": "^3.19.3",
+    "@midwayjs/core": "^3.19.0",
+    "@midwayjs/cron": "^3.19.2",
+    "@midwayjs/cross-domain": "^3.19.3",
+    "@midwayjs/decorator": "^3.19.0",
+    "@midwayjs/info": "^3.19.2",
+    "@midwayjs/koa": "^3.19.2",
     "@midwayjs/logger": "^3.4.0",
     "@midwayjs/orm": "^3.4.3",
-    "@midwayjs/static-file": "^3.16.1",
-    "@midwayjs/swagger": "^3.19.0",
-    "@midwayjs/typeorm": "^3.16.0",
-    "@midwayjs/upload": "^3.16.1",
-    "@midwayjs/validate": "^3.16.1",
-    "@midwayjs/view-ejs": "^3.16.1",
+    "@midwayjs/oss": "^3.19.3",
+    "@midwayjs/static-file": "^3.19.3",
+    "@midwayjs/swagger": "^3.19.2",
+    "@midwayjs/typeorm": "^3.19.2",
+    "@midwayjs/upload": "^3.19.3",
+    "@midwayjs/validate": "^3.19.2",
+    "@midwayjs/view-ejs": "^3.19.2",
     "axios": "^1.6.8",
     "bignumber.js": "^9.1.2",
     "cache-manager-ioredis-yet": "^2.0.4",
@@ -46,7 +47,7 @@
     "ws": "^8.17.0"
   },
   "devDependencies": {
-    "@midwayjs/mock": "^3.16.0",
+    "@midwayjs/mock": "^3.19.2",
     "@types/jest": "^29.5.12",
     "@types/koa": "^2.15.0",
     "@types/node": "20",

+ 10 - 0
src/comm/utils.ts

@@ -156,6 +156,16 @@ export class Utils {
     return _newTime;
   }
 
+  /**
+   * 获取当前时间
+   * @param newTime
+   */
+  formatNewTime(time = '', format = 'YYYY-MM-DD HH:mm:ss') {
+    moment.locale('zh-cn');
+    const _newTime = moment(time).format(format);
+    return _newTime;
+  }
+
   /**
    * 字段转驼峰法
    * @param obj

+ 16 - 1
src/config/config.max.ts

@@ -1,5 +1,6 @@
 import { CoolConfig } from '@cool-midway/core';
 import { MidwayConfig } from '@midwayjs/core';
+import { join } from 'path';
 
 /**
  * 本地开发 npm run dev 读取的配置文件
@@ -17,7 +18,8 @@ export default {
         // port: 3390,
         // username: 'root',
         // password: '123456',
-        database: 'va',
+        // database: 'va',
+        database: 'va_test_2025_3_5',
         // 自动建表 注意:线上部署的时候不要使用,有可能导致数据丢失
         synchronize: true,
         // 打印日志
@@ -44,4 +46,17 @@ export default {
     // 是否自动导入模块菜单
     initMenu: true,
   } as CoolConfig,
+  oss: {
+    // normal oss bucket
+    client: {
+      region: 'oss-cn-hongkong',
+      accessKeyId: 'LTAI5t7jxgSpcTPFi94JcBTe',
+      accessKeySecret: 'gIy7sNLflqjYkyyYLCwxQJKo6TYgw9',
+      bucket: 'easypay-legal-entity-demo',
+      endpoint: 'oss-cn-hongkong.aliyuncs.com',
+      timeout: '60s',
+    },
+  },
+  mode: 'file',
+  tmpdir: join(__dirname, '../tmp'), // 临时文件目录
 } as MidwayConfig;

+ 3 - 0
src/configuration.ts

@@ -15,6 +15,8 @@ import { IMidwayApplication } from '@midwayjs/core';
 import * as swagger from '@midwayjs/swagger';
 // import * as rpc from '@cool-midway/rpc';
 // import * as task from '@cool-midway/task';
+// import * as busboy from '@midwayjs/busboy';
+import * as oss from '@midwayjs/oss';
 
 @Configuration({
   imports: [
@@ -49,6 +51,7 @@ import * as swagger from '@midwayjs/swagger';
       component: info,
       enabledEnvironment: ['local', 'max'],
     },
+    oss     // 导入 oss 组件
   ],
   importConfigs: [join(__dirname, './config')],
 })

+ 92 - 0
src/modules/api/controller/admin/accounts.ts

@@ -0,0 +1,92 @@
+import { CoolController, BaseController } from '@cool-midway/core';
+import { Context } from '@midwayjs/koa';
+import {Fields, Files, Get, Provide, Query} from '@midwayjs/core';
+import {ALL, Body, Inject, Post} from "@midwayjs/decorator";
+import {EasyPayAdapter} from "../../../payment/adapter/easypay.adapter";
+import {EasyOpenService} from "../../service/open";
+import {accountsService} from "../../service/admin/accounts";
+import {OpenAccountEntity} from "../../entity/open_account";
+/**
+ * 开发接口的流水订单
+ */
+@Provide()
+@CoolController({
+  api: ['add', 'delete', 'update', 'info', 'list', 'page'],
+  entity: OpenAccountEntity,
+  pageQueryOp: {
+    where: async (ctx: Context) => {
+      const { merchant, roleId } = ctx.admin;
+      if ([1, 3].includes(roleId)) {
+        return [['mchId=:mchId', { mchId: merchant.mchId }]];
+      }
+      return [];
+    },
+  },
+})
+export class AccountsController extends BaseController {
+  @Inject()
+  ctx: Context; // Inject the context
+
+  @Inject()
+  easyPayAdapter: EasyPayAdapter;
+
+  @Inject()
+  easyOpenService: EasyOpenService;
+
+  @Inject()
+  accountsService: accountsService;
+
+  /**
+   * merchant_certification
+   */
+  @Post('/v1/upload', { summary: '上传文件' })
+  async upload(@Files() files: any[], @Fields() fields) {
+    const results = [];
+    for (const file of files) {
+      const result = await this.easyOpenService.upload(file);
+      results.push(result);
+    }
+    return { success: true, data: results };
+  }
+
+  /**
+   * 创建账户
+   */
+  @Post('/v3/accounts', { summary: '创建账户' })
+  async createPayInOrder(@Body(ALL) params: any) {
+    const method = this.ctx.method;
+    const path = this.ctx.path;
+    const res = await this.easyPayAdapter.request(method, "/v3/accounts", {
+      "request_id": `fusion-_${new Date().getTime()}`,
+      legal_entity: {
+        ...params,
+        "type": "COMPANY",
+        owner: {
+          ...params.owner,
+          "type": "PERSON",
+        },
+      }
+    });
+    if(res.hasOwnProperty('errors') && res.errors.length > 0) {
+      return this.fail("添加失败!")
+    }
+    // 创建商户
+    await this.accountsService.addAccounts(params, res)
+    return { success: true, data: {
+        ...res,
+        msg: '添加成功',
+    }};
+    // this.easyOpenService.save_user(res, params);
+    // return res;
+  }
+
+
+  /**
+   * 创建账户
+   */
+  @Get('accounts_list', { summary: '账户列表' })
+  async getAccountsList(@Query(ALL) params: any) {
+    const res = await this.easyPayAdapter.request("GET", "/v3/accounts", params);
+    return res;
+  }
+}

+ 196 - 0
src/modules/api/controller/admin/applications.ts

@@ -0,0 +1,196 @@
+import { CoolController, BaseController } from '@cool-midway/core';
+import { Context } from '@midwayjs/koa';
+import {Fields, Files, Get, Provide, Query} from '@midwayjs/core';
+import {ALL, Body, Inject, Post} from "@midwayjs/decorator";
+import {EasyPayAdapter} from "../../../payment/adapter/easypay.adapter";
+import {EasyOpenService} from "../../service/open";
+import {accountsService} from "../../service/admin/accounts";
+import {OpenApplicationsEntity} from "../../entity/open_applications";
+import { applicationsService } from '../../service/admin/applications';
+/**
+ * 开发接口的流水订单
+ */
+@Provide()
+@CoolController({
+  api: ['add', 'delete', 'update', 'info', 'list', 'page'],
+  entity: OpenApplicationsEntity,
+  pageQueryOp: {
+    where: async (ctx: Context) => {
+      const { merchant, roleId } = ctx.admin;
+      if ([1, 3].includes(roleId)) {
+        return [['mchId=:mchId', { mchId: merchant.mchId }]];
+      }
+      return [];
+    },
+  },
+})
+export class ApplicationsController extends BaseController {
+  @Inject()
+  ctx: Context; // Inject the context
+
+  @Inject()
+  easyPayAdapter: EasyPayAdapter;
+
+  @Inject()
+  easyOpenService: EasyOpenService;
+
+  @Inject()
+  accountsService: accountsService;
+  @Inject()
+  applicationsService: applicationsService;
+
+  /**
+   * merchant_certification
+   */
+  @Post('/v1/upload', { summary: '上传文件' })
+  async upload(@Files() files: any[], @Fields() fields) {
+    const results = [];
+    for (const file of files) {
+      const result = await this.easyOpenService.upload(file);
+      results.push(result);
+    }
+    return { success: true, data: results };
+  }
+
+  /**
+   * 申请收款账户
+   * {
+   *     "request_id": "fusion_0002",
+   *     "purpose": "ECOMM_PLATFORM",
+   *     "account_id": "2bd64372841a54bf8b41f879ff07884b",
+   *     "currency": "EUR",
+   *     "payment_type": "SWIFT",
+   *     "additional_info": {
+   *         "online_sales_method": "AMAZON",
+   *         "payer_party": "AMAZON",
+   *         "store_name": "redius",
+   *         "store_photos": ["2123321"],
+   *         "business_category": "纺织、服装及日化用品专门销售",
+   *         "store_address": {
+   *             "region": "HK",
+   *             "city": "HK",
+   *             "street": "HK"
+   *         }
+   *     }
+   * }
+   *
+   *
+   * {
+  id: '48a39dc9aec5417b883fcd3d5f9f5a0a',
+  request_id: 'fusionget_1740974452715',
+  reference: null,
+  account_id: '2bd64372841a54bf8b41f879ff07884b',
+  order_no: '20250303120053717551',
+  currency: 'EUR',
+  payment_type: 'SWIFT',
+  purpose: 'ECOMM_STANDALONE',
+  additional_info: {
+    store_name: '赚钱浜',
+    offline_sales_method: null,
+    online_sales_method: 'AMAZON,SHOPIFY',
+    website_url: 'va.fusion.com',
+    store_photos: [
+      'http://easypay-legal-entity-demo.oss-cn-hongkong.aliyuncs.com/fusion/uploads/1740973141012_9.png'
+    ],
+    business_category: '纺织、服装及日化用品专门销售',
+    business_regions: null,
+    monthly_transaction_count: 1000,
+    monthly_transaction_amount: null,
+    payer_party: 'AMAZON',
+    payer_party_account: '1231231',
+    funds_regions: null,
+    funds_type: 'INVESTMENT',
+    documents: null,
+    store_address: {
+      region: 'HK',
+      state_or_province: '香港',
+      city: '香港',
+      street: '香港西贡区市场街124号',
+      zip_code: '123123'
+    },
+    official_account: null,
+    official_account_appid: null,
+    mini_program: null,
+    mini_program_appid: null,
+    app_download_url: null,
+    app_appid: null,
+    abbreviated_company_name: null
+  },
+  status: 'PENDING',
+  reason: null,
+  create_time: '2025-03-03T12:00:53+08:00',
+  update_time: '2025-03-03T12:00:53+08:00'
+}
+   */
+  @Post('/applications', { summary: '申请收款账户' })
+  async createPayInOrder(@Body(ALL) params: any) {
+    return await this.applicationsService.addApplications(params, {
+      fail: this.fail,
+      ok: this.ok,
+    })
+    // this.easyOpenService.save_user(res, params);
+    // return res;
+  }
+
+  /**
+   * 创建账户
+   */
+  @Post('/get_applications_list', { summary: '创建账户' })
+  async getApplicationsList(@Body(ALL) params: any) {
+    // 获取申请列表
+
+    return await this.applicationsService.getApplicationsList(params);
+    // this.easyOpenService.save_user(res, params);
+    // return res;
+  }
+
+  /**
+   * 获取用户的账户余额
+   */
+  @Post('/getApplicationsListByMchId', { summary: '获取用户的账户余额' })
+  async getApplicationsListByMchId() {
+    // 获取申请列表
+
+    return await this.applicationsService.getApplicationsListByMchId();
+    // this.easyOpenService.save_user(res, params);
+    // return res;
+  }
+
+  /**
+   * 获取用户的账户流水
+   */
+  @Get('/getTransactionsListByMchId', { summary: '获取用户的账户流水' })
+  async getTransactionsListByMchId(@Query(ALL) params: any,) {
+    return await this.applicationsService.getTransactionsListByMchId(params);
+  }
+  /**
+   * 获取用户的收款账户
+   */
+  @Get('/getBankAccountsBy', { summary: '获取用户的收款账户' })
+  async getBankAccountsBy(@Query(ALL) params: any,) {
+    return await this.applicationsService.getBankAccountsBy(params);
+  }
+  /**
+   * 查询汇率
+   */
+  @Get('/getExchangeCurrency', { summary: '获取用户的收款账户' })
+  async getExchangeCurrency(@Query(ALL) params: any,) {
+    return await this.applicationsService.getExchangeCurrency(params);
+  }
+  /**
+   * 换汇
+   */
+  @Post('/exchanges', { summary: '获取用户的收款账户' })
+  async exchanges(@Body(ALL) params: any,) {
+    return await this.applicationsService.exchanges(params);
+  }
+  /**
+   * 转账
+   * 账户:b847628dec3e59509ac3d3f9d32ad533
+   */
+  @Post('/transfer', { summary: '获取用户的收款账户' })
+  async transfer(@Body(ALL) params: any,) {
+    return await this.applicationsService.transfer(params);
+  }
+
+}

+ 29 - 2
src/modules/api/controller/admin/openTransaction.ts

@@ -1,7 +1,11 @@
 import { CoolController, BaseController } from '@cool-midway/core';
 import { OpenPaymentOrderEntity } from '../../entity/open_payment_order';
 import { Context } from '@midwayjs/koa';
-import { Provide } from '@midwayjs/core';
+import {Fields, Files, Provide} from '@midwayjs/core';
+import {Inject, Post} from "@midwayjs/decorator";
+import {EasyPayAdapter} from "../../../payment/adapter/easypay.adapter";
+import {EasyOpenService} from "../../service/open";
+import {OpenPaymentAccountEntity} from "../../entity/open_payment_account";
 /**
  * 开发接口的流水订单
  */
@@ -19,4 +23,27 @@ import { Provide } from '@midwayjs/core';
     },
   },
 })
-export class OpenTransactionController extends BaseController {}
+export class OpenTransactionController extends BaseController {
+  @Inject()
+  ctx: Context; // Inject the context
+
+  @Inject()
+  easyPayAdapter: EasyPayAdapter;
+
+  @Inject()
+  easyOpenService: EasyOpenService;
+
+
+  /**
+   * merchant_certification
+   */
+  @Post('/v1/upload', { summary: '上传文件' })
+  async upload(@Files() files: any[], @Fields() fields) {
+    const results = [];
+    for (const file of files) {
+      const result = await this.easyOpenService.upload(file);
+      results.push(result);
+    }
+    return { success: true, data: results };
+  }
+}

+ 16 - 2
src/modules/api/controller/open.ts

@@ -17,7 +17,8 @@ import { IndividualEntity } from '../../payment/entity/individual';
 import { EasyPayAdapter } from '../../payment/adapter/easypay.adapter';
 import { EasyOpenService } from '../service/open';
 import { OrderType } from '../entity/open_payment_order';
-
+import {Fields, Files} from "@midwayjs/core";
+// import { FileItem } from '@midwayjs/upload';
 /**
  * 客户管理
  */
@@ -409,7 +410,7 @@ export class OpenApiController extends BaseController {
    * 创建换汇订单
    */
   @Post('/v1/exchanges', { summary: '创建换汇订单' })
-  async createdExchanges(@Query() params: any) {
+  async createdExchanges(@Body() params: any) {
     // Access the request method and path
     const method = this.ctx.method;
     const path = this.ctx.path;
@@ -1310,4 +1311,17 @@ export class OpenApiController extends BaseController {
     // return `${method}, ${path}, ${params}`
     return await this.easyPayAdapter.request(method, path, params);
   }
+
+  /**
+   * merchant_certification
+   */
+  @Post('/v1/upload', { summary: '上传文件' })
+  async upload(@Files() files: any[], @Fields() fields) {
+    const results = [];
+    for (const file of files) {
+      const result = await this.easyOpenService.upload(file);
+      results.push(result);
+    }
+    return { success: true, data: results };
+  }
 }

+ 1 - 1
src/modules/api/controller/webhook.ts

@@ -53,6 +53,6 @@ export class OpenApiWebhookController extends BaseController {
    **/
   @Post('/easypay-webhook/:type', { summary: 'easypay webhook' })
   async getBanks(@Param('type') type: any, @Body(ALL) params: any, ) {
-    return await this.openApiWebhookService.easypayWebhook(params, this.ctx, type);
+    return await this.openApiWebhookService.easypayWebhook(params, type);
   }
 }

+ 37 - 0
src/modules/api/entity/open_account.ts

@@ -0,0 +1,37 @@
+import { Column, Entity } from 'typeorm';
+import { BaseEntity } from '@cool-midway/core';
+import { OrderType } from './open_payment_order';
+
+export enum StatusType {
+  SUBMITTED = 'SUBMITTED', // 已提交
+  ACTIVE = 'ACTIVE', // 激活
+  FAILED = 'FAILED', // 失败
+  SUSPENDED = 'SUSPENDED', // 冻结
+  TERMINATED = 'TERMINATED', // 关闭
+}
+
+/**
+ * 收款账户
+ */
+@Entity('open_account')
+export class OpenAccountEntity extends BaseEntity {
+  @Column({ length: 255, comment: '商户编号', default: '' })
+  mch_id: string;
+
+  @Column({ length: 255, comment: '用户编号', default: '' })
+  account_id?: string;
+
+  @Column({ length: 255, comment: '企业名称', default: '' })
+  name?: string;
+  @Column({ length: 255, comment: '联系方式', default: '' })
+  phone?: string;
+  @Column({ length: 255, comment: '地址', default: '' })
+  address?: string;
+  @Column({ length: 255, comment: '公司类型', default: '' })
+  company_type?: string;
+  @Column({ length: 255, comment: '账户状态', default: '' })
+  status?: StatusType; // 使用枚举类型
+
+  @Column({ length: 255, comment: '客户来源', default: '' })
+  source?: string;
+}

+ 52 - 0
src/modules/api/entity/open_applications.ts

@@ -0,0 +1,52 @@
+import { Column, Entity } from 'typeorm';
+import { BaseEntity } from '@cool-midway/core';
+import { OrderType } from './open_payment_order';
+
+export enum StatusType {
+  PENDING = 'PENDING', // 已提交
+  SUCCESS = 'SUCCESS', // 激活
+  FAILED = 'FAILED', // 失败
+}
+
+export enum PurposeType {
+  ECOMM_PLATFORM = 'ECOMM_PLATFORM', // 线上电商平台收款
+  ECOMM_STANDALONE = 'ECOMM_STANDALONE', // 线上独立站收款
+  B2B = 'B2B', // B2B 收款
+  RETAIL = 'RETAIL', // 实体零售收款
+  WHOLESALE = 'WHOLESALE', // 实体批发收款
+  B2C_ONLINE = 'B2C_ONLINE',
+  B2C_ONLINE_OFFLINE = 'B2C_ONLINE_OFFLINE'
+}
+
+/**
+ * 收款账户
+ */
+@Entity('open_applications')
+export class OpenApplicationsEntity extends BaseEntity {
+  @Column({ length: 255, comment: '商户编号', default: '' })
+  mch_id: string;
+
+  @Column({ length: 255, comment: '用户编号', default: '' })
+  account_id?: string;
+
+  @Column({ length: 255, comment: '收款账户编号', default: '' })
+  application_id?: string;
+  
+  @Column({ length: 255, comment: '唯一申请编号', default: '' })
+  request_id?: string;
+
+  @Column({ length: 255, comment: '开户目的', default: '' })
+  purpose?: PurposeType;
+
+  @Column({ length: 255, comment: '币种', default: '' })
+  currency?: string;
+
+  @Column({ length: 255, comment: '支付方式', default: '' })
+  payment_type?: string;
+
+  @Column({ length: 255, comment: '账户状态', default: '' })
+  status?: StatusType; // 使用枚举类型
+
+  @Column({ length: 255, comment: '客户来源', default: '' })
+  source?: string;
+}

+ 18 - 0
src/modules/api/entity/open_payment_order.ts

@@ -75,5 +75,23 @@ export enum OrderType {
   PAYMENT = 'PAYMENT', // 付款
   TRANSFER = 'TRANSFER', // 转账
   DEPOSIT = 'DEPOSIT', // 入账
+  EXCHANGE = 'DEPOSIT', // 换汇
   TRANSACTION_FEE_ORDER = 'TRANSACTION_FEE_ORDER', // 交易手续费订单
 }
+
+export const OrderTypeEnum = {
+  CURRENCY_EXCHANGE: '换汇',
+  ACQUIRING_PAYMENT: '收单支付订单',
+  ACQUIRING_REFUND: '收单退款订单',
+  PAYMENT: '付款',
+  TRANSFER: '转账',
+  DEPOSIT: '入账',
+  EXCHANGE: '换汇',
+  TRANSACTION_FEE_ORDER: '交易手续费订单',
+};
+
+export const StatusTypeEnum = {
+  PENDING: '处理中',
+  SUCCESS: '成功',
+  FAILED: '失败',
+};

+ 1 - 1
src/modules/api/middleware/authority.ts

@@ -64,7 +64,7 @@ export class BaseAuthorityMiddleware
       }
 
       const params =
-        ctx?.req.method === 'GET'
+        ctx?.req.method === 'GET' || ctx.url.includes('/v1/upload')
           ? /* ctx?.request.query */ ''
           : ctx?.request.body;
       const merchantInfo = await this.merchantEntity.findOne({

+ 53 - 0
src/modules/api/service/admin/accounts.ts

@@ -0,0 +1,53 @@
+import {BaseService} from '@cool-midway/core';
+import {ILogger, Provide} from '@midwayjs/core';
+import {Inject} from '@midwayjs/decorator';
+import {InjectEntityModel} from '@midwayjs/typeorm';
+import {Repository} from 'typeorm';
+import {PayeeEntity} from '../../../payment/entity/payee';
+import {CustomerEntity} from '../../../payment/entity/customer';
+import {PaymentService} from '../../../payment/service/payment';
+import {SunPayAdapter} from '../../../payment/adapter/sunpay.adapter';
+import {OpenPaymentAccountEntity} from '../../entity/open_payment_account';
+import {OpenAccountEntity} from "../../entity/open_account";
+
+/**
+ * 描述
+ */
+@Provide()
+export class accountsService extends BaseService {
+  @InjectEntityModel(PayeeEntity)
+  payeeEntity: Repository<PayeeEntity>;
+  @InjectEntityModel(CustomerEntity)
+  customerEntity: Repository<CustomerEntity>;
+  @Inject()
+  paymentService: PaymentService;
+
+  @Inject()
+  sunPayAdapter: SunPayAdapter;
+
+  @Inject()
+  ctx;
+
+  @Inject()
+  logger: ILogger;
+
+  @InjectEntityModel(OpenPaymentAccountEntity)
+  openPaymentAccountEntity: Repository<OpenPaymentAccountEntity>;
+
+
+  @InjectEntityModel(OpenAccountEntity)
+  openAccountEntity: Repository<OpenAccountEntity>;
+
+  async addAccounts(params, res) {
+    // openPaymentAccountEntity.
+    const openUserObj = {
+      ...res.legal_entity,
+      mch_id: params.mchId,
+      account_id: res.id,
+      status: res.status || '',
+      source: 'EASYPAY',
+    };
+    delete openUserObj.id;
+    return await this.openAccountEntity.insert(openUserObj);
+  }
+}

+ 217 - 0
src/modules/api/service/admin/applications.ts

@@ -0,0 +1,217 @@
+import { BaseService } from '@cool-midway/core';
+import { ILogger, Inject, Provide } from '@midwayjs/core';
+import { InjectEntityModel } from '@midwayjs/typeorm';
+import { PayeeEntity } from '../../../payment/entity/payee';
+import { Repository } from 'typeorm';
+import { CustomerEntity } from '../../../payment/entity/customer';
+import { PaymentService } from '../../../payment/service/payment';
+import { SunPayAdapter } from '../../../payment/adapter/sunpay.adapter';
+import { OpenPaymentAccountEntity } from '../../entity/open_payment_account';
+import { OpenAccountEntity } from '../../entity/open_account';
+import { EasyPayAdapter } from '../../../payment/adapter/easypay.adapter';
+import { OpenApplicationsEntity } from '../../entity/open_applications';
+import { Utils } from '../../../../comm/utils';
+import { OrderTypeEnum, StatusTypeEnum } from '../../entity/open_payment_order';
+import * as md5 from 'md5';
+
+/**
+ * 开户管理
+ */
+@Provide()
+export class applicationsService extends BaseService {
+  @InjectEntityModel(PayeeEntity)
+  payeeEntity: Repository<PayeeEntity>;
+  @InjectEntityModel(CustomerEntity)
+  customerEntity: Repository<CustomerEntity>;
+  @Inject()
+  paymentService: PaymentService;
+
+  @Inject()
+  sunPayAdapter: SunPayAdapter;
+
+  @Inject()
+  ctx;
+
+  @Inject()
+  logger: ILogger;
+
+  @Inject()
+  utils: Utils;
+
+  @InjectEntityModel(OpenPaymentAccountEntity)
+  openPaymentAccountEntity: Repository<OpenPaymentAccountEntity>;
+
+  @InjectEntityModel(OpenAccountEntity)
+  openAccountEntity: Repository<OpenAccountEntity>;
+
+  @Inject()
+  easyPayAdapter: EasyPayAdapter;
+
+  @InjectEntityModel(OpenApplicationsEntity)
+  openApplicationsEntity: Repository<OpenApplicationsEntity>;
+
+  async addApplications(params, { fail, ok }) {
+    const mchInFo = await this.openAccountEntity.findOne({
+      where: {
+        mch_id: params.mchId,
+      },
+    });
+    console.log(58, mchInFo);
+
+    const applicationsParams = {
+      request_id: `fusionget_${new Date().getTime()}`,
+      ...params,
+      account_id: mchInFo.account_id,
+    };
+
+    const res = await this.easyPayAdapter.request(
+      'POST',
+      '/v3/applications',
+      applicationsParams
+    );
+    console.log(71, res);
+    if (res.hasOwnProperty('errors') && res.errors.length > 0) {
+      return fail(res.errors);
+    }
+    // 存入数据库
+    await this.openApplicationsEntity.insert({
+      mch_id: params.mch_id, //商户编号
+      account_id: mchInFo.account_id,
+      request_id: applicationsParams.request_id,
+      purpose: params.purpose, // 开户目的
+      currency: params.currency, // 币种
+      payment_type: params.payment_type, // 支付方式
+      status: res.data.status, // 申请状态
+      source: 'EASYPAY', // 来源
+      application_id: res.data.id,
+    });
+    return ok('添加成功');
+    // 创建商户
+  }
+
+  async getApplicationsList(params) {
+    const res = await this.easyPayAdapter.request(
+      'GET',
+      '/v3/applications',
+      params
+    );
+    return res;
+  }
+  /*
+  查询账户余额信息
+   */
+  async getApplicationsListByMchId() {
+    const merchantInfo = await this.getMerchantInfo()
+    const bank_accounts_res = await this.easyPayAdapter.request(
+      'GET',
+      `/v1/bank_accounts`,
+      {
+        page: 1,
+        size: 10,
+        account_id: merchantInfo.account_id,
+      }
+    );
+    const balances_res = await this.easyPayAdapter.request(
+      'GET',
+      `/v3/accounts/${merchantInfo.account_id}/balances`
+    );
+    bank_accounts_res.data = bank_accounts_res.data.map(elm => {
+      let amount = 0;
+      if (balances_res.data.length > 0) {
+        balances_res.data.forEach(item => {
+          if (!amount && item.currency === elm.currency) {
+            amount = item.amount;
+          }
+        });
+      }
+      return {
+        ...elm,
+        amount,
+      };
+    });
+    return bank_accounts_res;
+  }
+
+  async getTransactionsListByMchId(params) {
+    const merchantInfo = await this.getMerchantInfo()
+    const res = await this.easyPayAdapter.request(
+      'GET',
+      `/v3/accounts/${merchantInfo.account_id}/transactions`,
+      params
+    );
+    res.data = res.data.map(elm => {
+      return {
+        ...elm,
+        create_time: this.utils.formatNewTime(elm.create_time),
+        order_type: OrderTypeEnum[elm.order_type] || elm.order_type,
+        status: StatusTypeEnum[elm.status] || elm.status,
+      };
+    });
+    return res;
+  }
+
+  // 获取收款账户
+  async getBankAccountsBy(params) {
+    const merchantInfo = await this.getMerchantInfo()
+
+    const res = await this.easyPayAdapter.request('GET', `/v1/bank_accounts`, {
+      page: 1,
+      size: 10,
+      account_id: merchantInfo.account_id,
+    });
+    const bankAccountsIndex = res.data.findIndex(
+      elm => elm.currency === params.currency
+    );
+    return res.data[bankAccountsIndex];
+  }
+
+  // 查询汇率
+  async getExchangeCurrency(params) {
+    const res = await this.easyPayAdapter.request('GET', `/v1/exchange_rates`, params);
+    return res
+  }
+  // 换汇
+  async exchanges(params) {
+    const merchantInfo = await this.getMerchantInfo()
+    const res = await this.easyPayAdapter.request('POST', `/v1/exchanges`, {
+      account_id: merchantInfo.account_id,
+      ...params,
+      buy_amount:params.buy_amount * 100,
+    });
+    // 费率拦截
+    return res
+  }
+  // 转账
+  async transfer(params) {
+    console.log(186, params);
+    const to_merchantInfo = await this.getMerchantInfo(params.to_mch_id);
+    const merchantInfo = await this.getMerchantInfo();
+
+    const resParams = {
+      request_id: md5(`transfer_${merchantInfo.account_id}_${new Date().getTime()}`),
+      from_account_id: merchantInfo.account_id,
+      to_account_id: to_merchantInfo.account_id,
+      currency: params.currency,
+      amount: params.amount*100,
+      purpose: params.purpose
+    }
+    const res = await this.easyPayAdapter.request('POST', `/v1/transfers`, resParams);
+    console.log(202, resParams);
+    console.log(203, res);
+    return res
+  }
+
+  // 获取商户信息
+  async getMerchantInfo(mch_id= 'ep001@fusion.com') {
+    const merchantInfo = await this.openAccountEntity.findOne({
+      where: {
+        // mch_id: this.ctx.admin.merchant.mchId,
+        // mch_id: 'easypay@qq.com',
+        // mch_id: 'ep001@fusion.com',
+        // mch_id: 'easypay003@fusion.com',
+        mch_id
+      },
+    });
+    return merchantInfo
+  }
+}

+ 37 - 1
src/modules/api/service/open.ts

@@ -11,7 +11,7 @@ import {
 import { OpenPaymentAccountEntity } from '../entity/open_payment_account';
 import { EasyPayAdapter } from '../../payment/adapter/easypay.adapter';
 import * as md5 from 'md5';
-
+import { OSSService } from '@midwayjs/oss';
 /**
  * 描述
  */
@@ -33,6 +33,9 @@ export class EasyOpenService extends BaseService {
   easyPayAdapter: EasyPayAdapter;
 
 
+  @Inject()
+  ossService: OSSService;
+
   @Inject()
   ctx;
 
@@ -309,4 +312,37 @@ export class EasyOpenService extends BaseService {
       return res.data.status;
     }
   }
+
+
+  async upload (file: any) {
+    try {
+      // 生成 OSS 存储路径(带原始文件名)
+      const ossPath = `fusion/uploads/${Date.now()}_${file.filename}`;
+      // const result = await this.ossService.put('/test/test.log', localFile);
+
+      // 使用临时文件路径上传
+      const result = await this.ossService.put(
+        ossPath,
+        file.data,  // 直接使用 file.data 作为路径
+        // 如果需要设置 headers 可以添加第三个参数
+        { headers: { 'Content-Type': file.mimeType } }
+      );
+      console.log(file.data)
+      // await unlinkSync(file.data);
+
+
+      return {
+        url: result.url,
+        ossPath,
+        originalName: file.filename,
+        size: file.size
+      };
+    } catch (e) {
+
+
+    }
+
+
+  }
+
 }

+ 24 - 4
src/modules/api/service/webhook.ts

@@ -8,7 +8,6 @@ import { CustomerEntity } from '../../payment/entity/customer';
 import { PaymentService } from '../../payment/service/payment';
 import { PayeeEntity } from '../../payment/entity/payee';
 import { SunPayAdapter } from '../../payment/adapter/sunpay.adapter';
-import { Context } from '@midwayjs/koa';
 
 /**
  * 描述
@@ -31,14 +30,35 @@ export class OpenApiWebhookService extends BaseService {
   @Inject()
   logger: ILogger;
 
-  async easypayWebhook(params, ctx: Context, type: string) {
+  async easypayWebhook(params, type: string) {
     try {
       this.logger.info(
-        `easypay的webhook_${type}: ${this.ctx.admin.merchant.mchId}; params${params}`
+        `easypay的webhook_${type}: params${params}`
       );
+      // TODO 各类回调处理 后续处理
+      // 账户审核通过   account_approved
+      // 账户审核驳回   account_rejected
+      // 实名待更新     legal_entity_request_created
+      // 实名更新审核通过 legal_entity_request_success
+      // 实名更新审核驳回 legal_entity_request_rejected
+      // 收款账户申请成功 application_success
+      // 收款账户申请失败 application_failed
+      // 收款账户已下发   bank_account_created
+      // 收款账户信息变更 bank_account_updated
+      // 收单支付成功通知 acquiring_payment_success
+      // 收单支付成功通知 acquiring_refund_success
+      // 收单退款失败通知 acquiring_refund_failed
+      // 入账成功       deposit_success
+      // 换汇成功       exchange_success
+      // 换汇失败       exchange_failed
+      // 付款成功       payment_success
+      // 付款失败       payment_failed
+      // 付款银行退回    payment_refunded
+      // 转账成功        transfer_success
+      // 转账失败        transfer_failed
     } catch (err) {
       this.logger.error(
-        `easypay的webhook error ${type}: ${this.ctx.admin.merchant.mchId}; params${params}`
+        `easypay的webhook error ${type}: params${params}`
       );
     }
     this.ctx.status = 200;

+ 3 - 2
src/modules/payment/adapter/easypay.adapter.ts

@@ -155,6 +155,7 @@ export class EasyPayAdapter {
     let url = this.config.apiUrl;
     try {
       url = `${url}${endpoint.replace('/api/open', '')}`;
+      console.log(158, data)
       const accessToken = await this.config.tokenManager.getAccessToken();
       const axiosParams = {
         method,
@@ -173,8 +174,8 @@ export class EasyPayAdapter {
       //   console.log(response.data);
       //   throw new Error(`FusionPay API ${response.data.msg}`);
       // }
-      // console.log('response', response.data.data);
-      return response.data.data;
+      // console.log('response', response.data);
+      return response.data;
     } catch (error) {
 
       // console.log(error.response.data);

+ 2 - 0
src/modules/virtual/entity/bank.ts

@@ -30,6 +30,8 @@ export class BankEntity extends BaseEntity {
   isPobo: boolean;
   @Column({ default: false, comment: '商户ID' })
   merchantId: number;
+  @Column({ default: false, comment: '商户号ID' })
+  mchId: string;
 
   @Column({
     type: 'enum',

+ 20 - 1
src/modules/virtual/entity/order.ts

@@ -9,7 +9,15 @@ export enum OrderStatus {
   PROCESSING = 'PROCESSING', // 处理中
   COMPLETED = 'COMPLETED', // 交易完成
   FAILED = 'FAILED', // 交易失败
-  REJECTED = 'REJECTED', // 交易失败
+  REJECTED = 'REJECTED' // 交易驳回
+}
+
+/**
+ * 业务类型枚举
+ */
+export enum BusinessType {
+  DEPOSIT = 'DEPOSIT', //
+  WITHDRAW = 'WITHDRAW', //
 }
 
 /**
@@ -37,6 +45,9 @@ export class OrderEntity extends BaseEntity {
   @Column({ comment: '商户ID', nullable: true })
   merchantId: number;
 
+  @Column({ length: 100, nullable: true, comment: '商户名ID' })
+  mchId: string;
+
   @Column({ length: 10, comment: '币种', default: '' })
   currency?: string;
 
@@ -57,6 +68,14 @@ export class OrderEntity extends BaseEntity {
   @Column({ type: 'timestamp', comment: '交易完成时间' })
   completedAt: Date;
 
+  @Column({
+    type: 'enum',
+    enum: BusinessType,
+    default: BusinessType.DEPOSIT,
+    comment: '业务类型',
+  })
+  businessType: BusinessType;
+
   @ManyToOne(() => BankEntity, bank => bank.id)
   @JoinColumn({ name: 'bankId' })
   bank: BankEntity;