Merge branch 'pay' into test

# Conflicts:
#	cash-api/order-server/src/main/java/com/czg/mq/PrintMqListener.java
This commit is contained in:
2026-01-08 10:50:18 +08:00
96 changed files with 6633 additions and 19 deletions

View File

@@ -0,0 +1,75 @@
package com.czg.controller.admin;
import com.czg.EntryManager;
import com.czg.annotation.Debounce;
import com.czg.dto.req.AggregateMerchantDto;
import com.czg.dto.resp.BankBranchDto;
import com.czg.service.order.dto.AggregateMerchantVO;
import com.czg.service.order.service.ShopDirectMerchantService;
import com.czg.resp.CzgResult;
import com.czg.sa.StpKit;
import com.czg.task.EntryManagerTask;
import jakarta.annotation.Resource;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 进件管理
*
* @author ww
*/
@AllArgsConstructor
@RestController
@RequestMapping("/admin/data/entryManager")
public class EntryManagerController {
@Resource
private ShopDirectMerchantService shopDirectMerchantService;
@Resource
private EntryManagerTask entryManagerTask;
/**
* 查询银行支行列表
*
* @param province 省份 陕西省 从 /system/admin/common/region获取
* @param city 城市 西安市 从 /system/admin/common/region获取
* @param instId 顶级机构ID CMB 从 /system/admin/common/bankInfo 获取
*/
@GetMapping("bankBranchList")
public CzgResult<List<BankBranchDto>> queryBankBranchList(String province, String city, String instId) {
return CzgResult.success(EntryManager.queryBankBranchList(province, city, instId));
}
/**
* 获取进件信息
*/
@GetMapping
public CzgResult<AggregateMerchantVO> getEntry(Long shopId) {
return CzgResult.success(shopDirectMerchantService.getEntry(shopId));
}
/**
* 主动查询进件信息状态
* 进件状态是INIT 待处理 AUDIT 审核中 SIGN 待签约
* 3分钟内只能查一次
*/
@GetMapping
@Debounce(value = "#shopId", interval = 1000 * 60 * 3)
public CzgResult<Boolean> queryEntry(Long shopId) {
entryManagerTask.entryManager(shopId);
return CzgResult.success();
}
/**
* 申请进件
*/
@Debounce(value = "#reqDto.shopId")
@PostMapping
public CzgResult<Boolean> entryManager(@RequestBody AggregateMerchantDto reqDto) {
return CzgResult.success(shopDirectMerchantService.entryManager(reqDto));
}
}

View File

@@ -0,0 +1,124 @@
package com.czg.mq;
import cn.hutool.core.util.StrUtil;
import com.czg.EntryManager;
import com.czg.PayCst;
import com.czg.config.RabbitConstants;
import com.czg.config.RedisCst;
import com.czg.dto.resp.EntryRespDto;
import com.czg.order.entity.ShopDirectMerchant;
import com.czg.service.RedisService;
import com.czg.service.order.dto.AggregateMerchantVO;
import com.czg.service.order.service.ShopDirectMerchantService;
import com.rabbitmq.client.Channel;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.ThreadContext;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 打印mq消息处理器
*
* @author Administrator
*/
@Component
@Slf4j
public class EntryManagerMqListener {
@Resource
private RedisService redisService;
@Resource
private ShopDirectMerchantService shopDirectMerchantService;
String key = RedisCst.SHOP_ENTRY;
@RabbitListener(
bindings = @QueueBinding(
value = @Queue(value = "${spring.profiles.active}-" + RabbitConstants.Queue.SHOP_ENTRY_MANAGER,
durable = "true", exclusive = "false", autoDelete = "false"),
exchange = @Exchange(value = "${spring.profiles.active}-" + RabbitConstants.Exchange.CASH_EXCHANGE),
key = "${spring.profiles.active}-" + RabbitConstants.Queue.SHOP_ENTRY_MANAGER
),
concurrency = "5"
)
@RabbitHandler
public void handle(Message message, Channel channel, String msg) throws IOException {
String messageId = message.getMessageProperties().getMessageId();
if (hasMessageId(messageId)) {
return;
}
try {
Long shopId = Long.valueOf(msg);
// 将唯一标识添加到日志上下文
ThreadContext.put("traceId", messageId);
// 安全转换shopId
if (shopId == null) {
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
return;
}
AggregateMerchantVO entry = shopDirectMerchantService.getEntry(Long.valueOf(msg));
if (entry != null) {
EntryManager.uploadParamImage(entry);
List<String> platform = new ArrayList<>();
if (PayCst.EntryStatus.WAIT.equals(entry.getAlipayStatus())) {
platform.add(PayCst.Platform.ALIPAY);
}
if (PayCst.EntryStatus.WAIT.equals(entry.getWechatStatus())) {
platform.add(PayCst.Platform.WECHAT);
}
EntryRespDto resp = EntryManager.entryMerchant(entry, platform.toArray(new String[0]));
ShopDirectMerchant merchant = new ShopDirectMerchant();
merchant.setShopId(entry.getShopId());
merchant.setWechatStatus(resp.getWechatStatus());
merchant.setWechatErrorMsg(resp.getWechatErrorMsg());
merchant.setAlipayStatus(resp.getAlipayStatus());
merchant.setAlipayErrorMsg(resp.getAlipayErrorMsg());
shopDirectMerchantService.updateById(merchant);
}
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (Exception e) {
log.error("进件MQ对接业务异常shopId:{}", msg, e);
ShopDirectMerchant merchant = new ShopDirectMerchant();
merchant.setShopId(Long.valueOf(msg));
merchant.setWechatStatus(PayCst.EntryStatus.REJECTED);
merchant.setAlipayStatus(PayCst.EntryStatus.REJECTED);
merchant.setErrorMsg("系统错误,请联系管理员后重试。");
shopDirectMerchantService.updateById(merchant);
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
} finally {
delMessageId(messageId);
// 清除日志上下文信息
ThreadContext.remove("messageId");
}
}
public boolean hasMessageId(String messageId) {
if (!redisService.hasKey(key)) {
if (StrUtil.isNotBlank(messageId)) {
redisService.leftPush(key, messageId);
return false;
} else {
return true;
}
}
List<Object> list = redisService.lGet(key, 0, -1);
if (!list.contains(messageId)) {
redisService.leftPush(key, messageId);
return false;
}
return true;
}
public void delMessageId(String messageId) {
redisService.lRemove(key, 0, messageId);
}
}

View File

@@ -48,9 +48,6 @@ public class OrderMqListener {
orderInfoCustomService.updateOrderDetailStatus(Long.valueOf(finalInfo));
});
info = info.replace("UP_ORDER_DETAIL:", "");
System.out.println(info);
}
@RabbitListener(queues = {"${spring.profiles.active}-" + RabbitConstants.Queue.ORDER_STOCK_QUEUE})

View File

@@ -6,12 +6,13 @@ import com.czg.config.RabbitConstants;
import com.czg.config.RedisCst;
import com.czg.order.entity.MqLog;
import com.czg.order.service.MqLogService;
import com.czg.service.RedisService;
import com.czg.service.order.print.PrinterHandler;
import com.czg.service.order.utils.FunUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import java.util.function.Consumer;
@@ -27,16 +28,15 @@ public class PrintMqListener {
@Resource
private MqLogService mqLogService;
@Resource
private RedisService redisService;
private FunUtil funUtil;
@Lazy
@Resource
private PrinterHandler printerHandler;
private <T> void invokeFun(String type, String plat, T data, Consumer<T> consumer) {
private <T> void invokeFun(String queue, String type, String plat, T data, Consumer<T> consumer) {
long startTime = DateUtil.date().getTime();
log.info("接收到{}打印消息:{}", type, data);
MqLog mqLog = new MqLog().setQueue(RabbitConstants.Queue.ORDER_MACHINE_PRINT_QUEUE).setMsg(data.toString())
MqLog mqLog = new MqLog().setQueue(queue).setMsg(data.toString())
.setType(type).setPlat(plat).setCreateTime(DateUtil.date().toLocalDateTime());
try {
consumer.accept(data);
@@ -52,14 +52,14 @@ public class PrintMqListener {
@RabbitListener(queues = {"${spring.profiles.active}-" + RabbitConstants.Queue.ORDER_MACHINE_PRINT_QUEUE})
public void orderPrint(String req) {
// 执行核心打印逻辑
invokeFun("orderPrint", "java.order", req, (data) -> {
invokeFun(RabbitConstants.Queue.ORDER_MACHINE_PRINT_QUEUE, "orderPrint", "java.order", req, (data) -> {
JSONObject jsonObject = JSONObject.parseObject(data);
String orderId = jsonObject.getString("orderId");
if (orderId == null) {
throw new RuntimeException("订单打印失败未传递orderId");
}
Boolean printOrder = jsonObject.getBoolean("printOrder");
redisService.runFunAndCheckKey(() -> {
funUtil.runFunAndCheckKey(() -> {
printerHandler.handler(orderId, printOrder != null && !printOrder ? PrinterHandler.PrintTypeEnum.ONE : PrinterHandler.PrintTypeEnum.ONE_AND_ORDER);
return null;
}, RedisCst.getLockKey("orderPrint", orderId));
@@ -71,14 +71,16 @@ public class PrintMqListener {
*/
@RabbitListener(queues = {"${spring.profiles.active}-" + RabbitConstants.Queue.ORDER_HANDOVER_PRINT_QUEUE})
public void handoverPrint(String id) {
invokeFun("handoverPrint", "java.order", id, (data) -> printerHandler.handler(data, PrinterHandler.PrintTypeEnum.HANDOVER));
invokeFun(RabbitConstants.Queue.ORDER_HANDOVER_PRINT_QUEUE, "handoverPrint", "java.order", id, (data) ->
printerHandler.handler(data, PrinterHandler.PrintTypeEnum.HANDOVER));
}
/**
* 叫号打印
*/
@RabbitListener(queues = {"${spring.profiles.active}-" + RabbitConstants.Queue.CALL_TABLE_PRINT_QUEUE})
@RabbitListener(queues = {"${spring.profiles.active}-" + RabbitConstants.Queue.CALL_TABLE_QUEUE})
public void callTablePrint(String id) {
invokeFun("handoverPrint", "java.order", id, (data) -> printerHandler.handler(data, PrinterHandler.PrintTypeEnum.CALL));
invokeFun(RabbitConstants.Queue.CALL_TABLE_QUEUE, "callTable", "java.order", id, (data) ->
printerHandler.handler(data, PrinterHandler.PrintTypeEnum.CALL));
}
}

View File

@@ -0,0 +1,87 @@
package com.czg.task;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.czg.EntryManager;
import com.czg.PayCst;
import com.czg.account.entity.ShopInfo;
import com.czg.account.service.ShopInfoService;
import com.czg.dto.resp.QueryStatusResp;
import com.czg.order.entity.ShopDirectMerchant;
import com.czg.order.service.ShopOrderStatisticService;
import com.czg.order.service.ShopProdStatisticService;
import com.czg.order.service.ShopTableOrderStatisticService;
import com.czg.service.RedisService;
import com.czg.service.order.service.ShopDirectMerchantService;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
/**
* 进件查询
*
* @author ww
*/
@Component
@Slf4j
public class EntryManagerTask {
@Resource
private ShopDirectMerchantService shopDirectMerchantService;
@DubboReference
private ShopInfoService shopInfoService;
//每10分钟查一次
@Scheduled(cron = "0 0/10 * * * ? ")
public void run() {
log.info("进件查询,定时任务执行");
long start = System.currentTimeMillis();
entryManager(null);
log.info("进件查询,定时任务执行完毕,耗时:{}ms", start - System.currentTimeMillis());
}
/**
* 查询状态为待处理、待签约、待审核的进件
*/
public void entryManager(Long shopId) {
List<ShopDirectMerchant> list = shopDirectMerchantService.list(QueryWrapper.create()
.eq(ShopDirectMerchant::getShopId, shopId)
.in(ShopDirectMerchant::getWechatStatus, PayCst.EntryStatus.NEED_QUERY_LIST)
.or(ShopDirectMerchant::getAlipayStatus).in(PayCst.EntryStatus.NEED_QUERY_LIST));
if (CollUtil.isEmpty(list)) {
return;
}
for (ShopDirectMerchant shopDirectMerchant : list) {
String wechatMerchantId = "";
String alipayMerchantId = "";
if (PayCst.EntryStatus.NEED_QUERY_LIST.contains(shopDirectMerchant.getWechatStatus())) {
QueryStatusResp wechatStatus = EntryManager.queryWechatEntryStatus(shopDirectMerchant.getMerchantCode());
shopDirectMerchant.setWechatStatus(wechatStatus.getStatus());
shopDirectMerchant.setWechatErrorMsg(wechatStatus.getFailReason());
shopDirectMerchant.setWechatSignUrl(wechatStatus.getSignUrl());
if (PayCst.EntryStatus.FINISH.equals(wechatStatus.getStatus())) {
wechatMerchantId = wechatStatus.getThirdMerchantId();
}
}
if (PayCst.EntryStatus.NEED_QUERY_LIST.contains(shopDirectMerchant.getAlipayStatus())) {
QueryStatusResp alipayStatus = EntryManager.queryAlipayEntryStatus(shopDirectMerchant.getMerchantCode());
shopDirectMerchant.setAlipayStatus(alipayStatus.getStatus());
shopDirectMerchant.setAlipayErrorMsg(alipayStatus.getFailReason());
shopDirectMerchant.setAlipaySignUrl(alipayStatus.getSignUrl());
if (PayCst.EntryStatus.FINISH.equals(alipayStatus.getStatus())) {
alipayMerchantId = alipayStatus.getThirdMerchantId();
}
}
shopDirectMerchantService.updateById(shopDirectMerchant);
if (StrUtil.isNotBlank(wechatMerchantId) || StrUtil.isNotBlank(alipayMerchantId)) {
shopInfoService.editEntry(shopDirectMerchant.getShopId(), wechatMerchantId, alipayMerchantId);
}
}
}
}

View File

@@ -28,6 +28,7 @@ import java.util.List;
/**
* 订单过期处理
* 退款失败 补偿
*
* @author ww
*/

View File

@@ -0,0 +1,61 @@
package com.czg.controller.admin;
import com.czg.BaseQueryParam;
import com.czg.resp.CzgResult;
import com.czg.system.entity.SysBankInfo;
import com.czg.system.entity.SysCategoryInfo;
import com.czg.system.entity.SysRegion;
import com.czg.system.service.SysBankInfoService;
import com.czg.system.service.SysCategoryInfoService;
import com.czg.system.service.SysRegionService;
import com.czg.system.vo.SysCategoryInfoVO;
import com.mybatisflex.core.paginate.Page;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
/**
* 通用
*
* @author Administrator
*/
@RestController
@RequestMapping("/admin/common")
public class SysCommonController {
@Resource
private SysRegionService sysRegionService;
@Resource
private SysBankInfoService bankInfoService;
@Resource
private SysCategoryInfoService categoryInfoService;
/**
* 获取所有地域
*/
@GetMapping("/region")
public CzgResult<List<SysRegion>> region() {
return CzgResult.success(sysRegionService.regionList());
}
/**
* 获取银行信息
*/
@GetMapping("/bankInfo")
public CzgResult<Page<SysBankInfo>> bankInfo(BaseQueryParam param, @RequestParam String bankName) {
return CzgResult.success(bankInfoService.bankInfoList(param, bankName));
}
/**
* 类目信息表
*/
@GetMapping("/category")
public CzgResult<List<SysCategoryInfoVO>> category() {
return CzgResult.success(categoryInfoService.categoryList());
}
}

View File

@@ -21,12 +21,24 @@ public class RabbitConfig {
@Value("${spring.profiles.active}")
private String activeProfile;
@Bean
@Primary
public DirectExchange directExchange() {
return new DirectExchange(activeProfile + "-" + RabbitConstants.Exchange.CASH_EXCHANGE);
}
//------------------------------------------------------ 商家入驻
@Bean
public Queue entryManagerQueue() {
return new Queue(activeProfile + "-" + RabbitConstants.Queue.SHOP_ENTRY_MANAGER, true, false, false);
}
@Bean
public Binding entryManagerExchange(Queue entryManagerQueue, DirectExchange exchange) {
return BindingBuilder.bind(entryManagerQueue).to(exchange).with(activeProfile + "-" + RabbitConstants.Queue.SHOP_ENTRY_MANAGER);
}
//------------------------------------------------------订单打印队列
@Bean
public Queue orderPrintQueue() {
@@ -36,6 +48,7 @@ public class RabbitConfig {
args.put("x-message-ttl", 180000);
return new Queue(activeProfile + "-" + RabbitConstants.Queue.ORDER_PRINT_QUEUE, true, false, false, args);
}
@Bean
public Binding bindingOrderPrintExchange(Queue orderPrintQueue, DirectExchange exchange) {
return BindingBuilder.bind(orderPrintQueue).to(exchange).with(activeProfile + "-" + RabbitConstants.Queue.ORDER_PRINT_QUEUE);
@@ -51,6 +64,7 @@ public class RabbitConfig {
// args.put("x-message-ttl", 180000);
return new Queue(activeProfile + "-" + RabbitConstants.Queue.ORDER_MACHINE_PRINT_QUEUE, true, false, false);
}
@Bean
public Binding bindingOrderMachinePrintExchange(Queue orderMachinePrintQueue, DirectExchange exchange) {
return BindingBuilder.bind(orderMachinePrintQueue).to(exchange).with(activeProfile + "-" + RabbitConstants.Queue.ORDER_MACHINE_PRINT_QUEUE);
@@ -61,6 +75,7 @@ public class RabbitConfig {
public Queue handoverPrintQueue() {
return new Queue(activeProfile + "-" + RabbitConstants.Queue.ORDER_HANDOVER_PRINT_QUEUE, true, false, false);
}
@Bean
public Binding bindingHandoverPrintExchange(Queue handoverPrintQueue, DirectExchange exchange) {
return BindingBuilder.bind(handoverPrintQueue).to(exchange).with(activeProfile + "-" + RabbitConstants.Queue.ORDER_HANDOVER_PRINT_QUEUE);
@@ -69,11 +84,12 @@ public class RabbitConfig {
//------------------------------------------------------叫号 打票
@Bean
public Queue callTablePrintQueue() {
return new Queue(activeProfile + "-" + RabbitConstants.Queue.CALL_TABLE_PRINT_QUEUE, true, false, false);
return new Queue(activeProfile + "-" + RabbitConstants.Queue.CALL_TABLE_QUEUE, true, false, false);
}
@Bean
public Binding bindingCallTablePrintExchange(Queue callTablePrintQueue, DirectExchange exchange) {
return BindingBuilder.bind(callTablePrintQueue).to(exchange).with(activeProfile + "-" + RabbitConstants.Queue.CALL_TABLE_PRINT_QUEUE);
return BindingBuilder.bind(callTablePrintQueue).to(exchange).with(activeProfile + "-" + RabbitConstants.Queue.CALL_TABLE_QUEUE);
}
//------------------------------------------------------订单取消
@@ -81,9 +97,10 @@ public class RabbitConfig {
public Queue orderCancelQueue() {
return new Queue(activeProfile + "-" + RabbitConstants.Queue.ORDER_CANCEL_QUEUE, true);
}
@Bean
public Binding bindingOrderCancelExchange(Queue orderPrintQueue, DirectExchange exchange) {
return BindingBuilder.bind(orderPrintQueue).to(exchange).with(activeProfile + "-" + RabbitConstants.Queue.ORDER_CANCEL_QUEUE);
public Binding bindingOrderCancelExchange(Queue orderCancelQueue, DirectExchange exchange) {
return BindingBuilder.bind(orderCancelQueue).to(exchange).with(activeProfile + "-" + RabbitConstants.Queue.ORDER_CANCEL_QUEUE);
}
//------------------------------------------------------ 订单库存更新
@@ -91,6 +108,7 @@ public class RabbitConfig {
public Queue orderStockQueue() {
return new Queue(activeProfile + "-" + RabbitConstants.Queue.ORDER_STOCK_QUEUE, true);
}
@Bean
public Binding bindingOrderStockExchange(Queue orderStockQueue, DirectExchange exchange) {
return BindingBuilder.bind(orderStockQueue).to(exchange).with(activeProfile + "-" + RabbitConstants.Queue.ORDER_STOCK_QUEUE);
@@ -102,6 +120,7 @@ public class RabbitConfig {
public Queue productInfoChangeQueue() {
return new Queue(activeProfile + "-" + RabbitConstants.Queue.PRODUCT_INFO_CHANGE_QUEUE, true);
}
@Bean
public Binding bindingProductInfoChange(Queue productInfoChangeQueue, DirectExchange exchange) {
return BindingBuilder.bind(productInfoChangeQueue).to(exchange).with(activeProfile + "-" + RabbitConstants.Queue.PRODUCT_INFO_CHANGE_QUEUE);
@@ -112,6 +131,7 @@ public class RabbitConfig {
public Queue orderRefundQueue() {
return new Queue(activeProfile + "-" + RabbitConstants.Queue.ORDER_REFUND_QUEUE, true);
}
@Bean
public Binding bindingOrderRefundExchange(Queue orderRefundQueue, DirectExchange exchange) {
return BindingBuilder.bind(orderRefundQueue).to(exchange).with(activeProfile + "-" + RabbitConstants.Queue.ORDER_REFUND_QUEUE);
@@ -119,6 +139,7 @@ public class RabbitConfig {
//------------------------------------------------------ 申请短信模板队列
/**
* 1,2,applySmsTemp 模版审核
* 1,2,sendMarkSms 发送营销短信
@@ -129,16 +150,18 @@ public class RabbitConfig {
public Queue applySmsTemplateQueue() {
return new Queue(activeProfile + "-" + RabbitConstants.Queue.APPLY_SMS_TEMPLATE_QUEUE, true);
}
@Bean
public Binding bindingApplySmsTemplateExchange(Queue applySmsTemplateQueue, DirectExchange exchange) {
return BindingBuilder.bind(applySmsTemplateQueue).to(exchange).with(activeProfile + "-" + RabbitConstants.Queue.APPLY_SMS_TEMPLATE_QUEUE);
}
//------------------------------------------------------ 生日礼品短信队列
//------------------------------------------------------ 生日礼品短信队列
@Bean
public Queue birthdayGiftSmsQueue() {
return new Queue(activeProfile + "-" + RabbitConstants.Queue.BIRTHDAY_GIFT_SMS_QUEUE, true);
}
@Bean
public Binding bindingBirthdayGiftSmsExchange(Queue birthdayGiftSmsQueue, DirectExchange exchange) {
return BindingBuilder.bind(birthdayGiftSmsQueue).to(exchange).with(activeProfile + "-" + RabbitConstants.Queue.BIRTHDAY_GIFT_SMS_QUEUE);
@@ -149,6 +172,7 @@ public class RabbitConfig {
public Queue orderProductStatusQueue() {
return new Queue(activeProfile + "-" + RabbitConstants.Queue.ORDER_PRODUCT_STATUS_QUEUE, true);
}
@Bean
public Binding bindingOrderProductStatusExchange(Queue orderProductStatusQueue, DirectExchange exchange) {
return BindingBuilder.bind(orderProductStatusQueue).to(exchange).with(activeProfile + "-" + RabbitConstants.Queue.ORDER_PRODUCT_STATUS_QUEUE);
@@ -160,6 +184,7 @@ public class RabbitConfig {
public Queue orderDetailStatusQueue() {
return new Queue(activeProfile + "-" + RabbitConstants.Queue.ORDER_DETAIL_STATUS_QUEUE, true);
}
@Bean
public Binding bindingOrderDetailStatusExchange(Queue orderDetailStatusQueue, DirectExchange exchange) {
return BindingBuilder.bind(orderDetailStatusQueue).to(exchange).with(activeProfile + "-" + RabbitConstants.Queue.ORDER_DETAIL_STATUS_QUEUE);

View File

@@ -10,13 +10,14 @@ public interface RabbitConstants {
}
class Queue {
public static final String SHOP_ENTRY_MANAGER = "shop.entry.manager";
public static final String ORDER_STOCK_QUEUE = "order.stock.queue";
public static final String ORDER_REFUND_QUEUE = "order.refund.queue";
public static final String ORDER_CANCEL_QUEUE = "order.cancel.queue";
public static final String ORDER_PRINT_QUEUE = "order.print.queue";
public static final String ORDER_MACHINE_PRINT_QUEUE = "order.machine.print.queue";
public static final String ORDER_HANDOVER_PRINT_QUEUE = "order.handover.print.queue";
public static final String CALL_TABLE_PRINT_QUEUE = "call.table.print.queue";
public static final String CALL_TABLE_QUEUE = "call.table.print.queue";
public static final String PRODUCT_INFO_CHANGE_QUEUE = "product.info.change.queue";
/**

View File

@@ -130,6 +130,13 @@ public class RabbitPublisher {
sendMsg(RabbitConstants.Queue.ORDER_PRODUCT_STATUS_QUEUE, qrContent);
}
/**
* 订单商品状态消息
*/
public void sendEntryManagerMsg(String shopId) {
sendMsg(RabbitConstants.Queue.SHOP_ENTRY_MANAGER, shopId);
}
/**
* 订单商品状态消息

View File

@@ -33,6 +33,8 @@ public interface RedisCst {
public static final String EXPIRED_WECHAT = "expired:wechat:";
}
//商家进件
String SHOP_ENTRY = "shop:entry";
String SMS_CODE = "sms:code:";
// 店铺会员动态支付码

View File

@@ -142,6 +142,14 @@ public class ShopInfo implements Serializable {
* -1 平台禁用 0-过期1正式营业
*/
private Integer status;
/**
* 微信商户id
*/
private String wechatMerchantId;
/**
* 支付宝商户id
*/
private String alipayMerchantId;
/**
* 到期时间

View File

@@ -37,6 +37,11 @@ public interface ShopInfoService extends IService<ShopInfo> {
Boolean edit(ShopInfoEditDTO shopInfoEditDTO);
/**
* 进件结果保存
*/
Boolean editEntry(Long shopId, String wechatMerchantId, String alipayMerchantId);
ShopDetailDTO detail(Long id) throws CzgException;
ShopInfoByCodeDTO getByCode(String tableCode, String lat, String lng, boolean checkState);

View File

@@ -0,0 +1,111 @@
package com.czg.order.entity;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.Table;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 商户进件 实体类。
*
* @author ww
* @since 2026-01-07
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("tb_shop_direct_merchant")
public class ShopDirectMerchant implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 店铺id
*/
@Id
private Long shopId;
/**
* 营业执照编号
*/
private String licenceNo;
/**
* 商户编号(在当前系统唯一)
*/
private String merchantCode;
/**
* 商户基础信息
*/
private String merchantBaseInfo;
/**
* 法人信息
*/
private String legalPersonInfo;
/**
* 营业执照信息
*/
private String businessLicenceInfo;
/**
* 门店信息
*/
private String storeInfo;
/**
* 结算信息
*/
private String settlementInfo;
@Column(onInsertValue = "now()")
private LocalDateTime createTime;
@Column(onInsertValue = "now()", onUpdateValue = "now()")
private LocalDateTime updateTime;
private String errorMsg;
/**
* 微信状态
*/
private String wechatStatus;
/**
* 微信进件错误信息
*/
private String wechatErrorMsg;
/**
* 微信进件签名地址
*/
private String wechatSignUrl;
/**
* 支付宝状态
*/
private String alipayStatus;
/**
* 支付宝进件错误信息
*/
private String alipayErrorMsg;
/**
* 支付宝进件签名地址
*/
private String alipaySignUrl;
}

View File

@@ -0,0 +1,82 @@
package com.czg.system.entity;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import java.io.Serializable;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 银行账户信息表 实体类。
*
* @author ww
* @since 2026-01-06
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("sys_bank_info")
public class SysBankInfo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@Id(keyType = KeyType.Auto)
private BigInteger id;
/**
* 银行名称
*/
private String accountBank;
/**
* 银行数字编码
*/
private Integer accountBankCode;
/**
* 银行别名
*/
private String bankAlias;
/**
* 银行别名编码
*/
private String bankAliasCode;
/**
* 不知道干啥的
*/
private Boolean needBankBranch;
/**
* 银行英文编码
*/
private String bankCode;
/**
* 创建时间
*/
@Column(onInsertValue = "now()")
private LocalDateTime createTime;
/**
* 更新时间
*/
@Column(onInsertValue = "now()", onUpdateValue = "now()")
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,77 @@
package com.czg.system.entity;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import java.io.Serializable;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 类目信息表 实体类。
*
* @author ww
* @since 2026-01-06
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("sys_category_info")
public class SysCategoryInfo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@Id(keyType = KeyType.Auto)
private BigInteger id;
/**
* 一级类目
*/
private String firstCategory;
/**
* 二级类目code
*/
private String secondCategoryCode;
/**
* 二级类目
*/
private String secondCategory;
/**
* 适用商家说明
*/
private String applicableMerchant;
/**
* 特殊资质(为空则无需提供)
*/
private String specialQualification;
/**
* 创建时间
*/
@Column(onInsertValue = "now()")
private LocalDateTime createTime;
/**
* 更新时间
*/
@Column(onInsertValue = "now()", onUpdateValue = "now()")
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,70 @@
package com.czg.system.entity;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.Table;
import java.io.Serializable;
import java.io.Serial;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 行政区表 实体类。
*
* @author ww
* @since 2026-01-06
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("sys_region")
public class SysRegion implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 区域编码
*/
@Id
private String regionId;
/**
* 上级区域编码
*/
private String parentRegionId;
/**
* 区域级别1=国家2=省3=市4=县
*/
@Id
private Integer regionLevel;
/**
* 区域中文全称
*/
private String regionName;
/**
* 区域拼音
*/
private String regionPinyin;
/**
* 全级别名称(如“中国|北京|北京市|东城区)
*/
private String fullName;
/**
* 子级区域
*/
@Column(ignore = true)
private List<SysRegion> children;
}

View File

@@ -0,0 +1,19 @@
package com.czg.system.service;
import com.czg.BaseQueryParam;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.service.IService;
import com.czg.system.entity.SysBankInfo;
import java.util.List;
/**
* 银行账户信息表 服务层。
*
* @author ww
* @since 2026-01-06
*/
public interface SysBankInfoService extends IService<SysBankInfo> {
Page<SysBankInfo> bankInfoList(BaseQueryParam param, String bankName);
}

View File

@@ -0,0 +1,20 @@
package com.czg.system.service;
import com.czg.system.vo.SysCategoryInfoVO;
import com.mybatisflex.core.service.IService;
import com.czg.system.entity.SysCategoryInfo;
import java.util.List;
import java.util.Map;
/**
* 类目信息表 服务层。
*
* @author ww
* @since 2026-01-06
*/
public interface SysCategoryInfoService extends IService<SysCategoryInfo> {
List<SysCategoryInfoVO> categoryList();
}

View File

@@ -0,0 +1,17 @@
package com.czg.system.service;
import com.mybatisflex.core.service.IService;
import com.czg.system.entity.SysRegion;
import java.util.List;
/**
* 行政区表 服务层。
*
* @author ww
* @since 2026-01-06
*/
public interface SysRegionService extends IService<SysRegion> {
List<SysRegion> regionList();
}

View File

@@ -0,0 +1,40 @@
package com.czg.system.vo;
import com.czg.system.entity.SysCategoryInfo;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.util.List;
/**
* 类目信息表 实体类。
*
* @author ww
* @since 2026-01-06
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SysCategoryInfoVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 一级类目
*/
private String firstCategory;
private List<SysCategoryInfo> child;
}

View File

@@ -42,6 +42,8 @@
<pinyin.version>2.5.1</pinyin.version>
<IJPay.version>2.9.10</IJPay.version>
<netty.version>4.1.128.Final</netty.version>
<wechatpay.version>0.2.17</wechatpay.version>
<apipay-v3.version>3.1.65.ALL</apipay-v3.version>
</properties>
<dependencyManagement>
@@ -268,6 +270,18 @@
<artifactId>netty-codec-mqtt</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>${wechatpay.version}</version>
</dependency>
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java-v3</artifactId>
<version>${apipay-v3.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.czg</groupId>
<artifactId>cash-sdk</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>aggregation-pay</artifactId>
<packaging>jar</packaging>
<name>聚合支付</name>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
</dependency>
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java-v3</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,458 @@
package com.czg;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.czg.dto.req.*;
import com.czg.dto.resp.BankBranchDto;
import com.czg.dto.resp.EntryRespDto;
import com.czg.dto.resp.EntryThirdRespDto;
import com.czg.dto.resp.QueryStatusResp;
import com.czg.exception.CzgException;
import com.czg.third.alipay.AlipayEntryManager;
import com.czg.third.wechat.WechatEntryManager;
import com.czg.utils.AssertUtil;
import com.czg.utils.AsyncTaskExecutor;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
/**
* 进件管理
*
* @author yjjie
* @date 2026/1/6 13:56
*/
public class EntryManager {
/**
* 查询银行支行列表
*
* @param province 省份 陕西省
* @param city 城市 西安市
* @param instId 顶级机构ID CMB
*/
public static List<BankBranchDto> queryBankBranchList(String province, String city, String instId) {
return AlipayEntryManager.queryBankBranchList(null, instId, province, city);
}
/**
* 查询微信进件状态
*
* @param merchantCode 商户编号
* @return 进件状态
*/
public static QueryStatusResp queryWechatEntryStatus(String merchantCode) {
return WechatEntryManager.queryMerchantEntryStatus(null, merchantCode);
}
/**
* 查询支付宝进件状态
*
* @param merchantCode 商户编号
* @return 进件状态
*/
public static QueryStatusResp queryAlipayEntryStatus(String merchantCode) {
return AlipayEntryManager.queryMerchantEntryStatus(null, merchantCode);
}
/**
* 进件
* 请先执行:
* 1. {@link com.czg.EntryManager#verifyEntryParam(AggregateMerchantDto)} 验证进件参数
* 2. {@link com.czg.EntryManager#uploadParamImage(AggregateMerchantDto)} 上传图片至第三方
*
* @param reqDto 进件参数
* @param platform 平台 {@link com.czg.PayCst.Platform}
*/
public static EntryRespDto entryMerchant(AggregateMerchantDto reqDto, String... platform) {
List<Supplier<EntryThirdRespDto>> tasks = new ArrayList<>();
if (platform == null || platform.length == 0) {
platform = new String[]{PayCst.Platform.WECHAT, PayCst.Platform.ALIPAY};
}
if (ArrayUtil.contains(platform, PayCst.Platform.WECHAT)) {
tasks.add(() -> WechatEntryManager.entryMerchant(null, reqDto));
}
if (ArrayUtil.contains(platform, PayCst.Platform.ALIPAY)) {
tasks.add(() -> AlipayEntryManager.entryMerchant(null, reqDto));
}
// 执行所有任务
List<AsyncTaskExecutor.TaskResult<EntryThirdRespDto>> results = AsyncTaskExecutor.executeAll(tasks);
return getEntryRespDto(results);
}
@NotNull
private static EntryRespDto getEntryRespDto(List<AsyncTaskExecutor.TaskResult<EntryThirdRespDto>> results) {
EntryRespDto entryRespDto = new EntryRespDto();
for (AsyncTaskExecutor.TaskResult<EntryThirdRespDto> result : results) {
// 合并两个进件结果
EntryThirdRespDto respDto = result.result();
if (PayCst.Platform.WECHAT.equals(respDto.getPlatform())) {
entryRespDto.setWechatApplyId(respDto.getEntryId());
entryRespDto.setWechatStatus(respDto.getStatus());
entryRespDto.setWechatErrorMsg(respDto.getErrorMsg());
} else if (PayCst.Platform.ALIPAY.equals(respDto.getPlatform())) {
entryRespDto.setAlipayOrderId(respDto.getEntryId());
entryRespDto.setAlipayStatus(respDto.getStatus());
entryRespDto.setAlipayErrorMsg(respDto.getErrorMsg());
}
}
return entryRespDto;
}
/**
* 上传图片至第三方
* 请先执行 {@link com.czg.EntryManager#verifyEntryParam(AggregateMerchantDto)} 验证进件参数
*
* @param reqDto 进件参数
*/
public static void uploadParamImage(AggregateMerchantDto reqDto) {
List<Supplier<Void>> tasks = new ArrayList<>();
MerchantBaseInfoDto baseInfo = reqDto.getMerchantBaseInfo();
// 联系人身份证反面
tasks.add(() -> {
uploadImageToThird(baseInfo.getContactIdCardBackPic());
return null;
});
// 联系人身份证正面
tasks.add(() -> {
uploadImageToThird(baseInfo.getContactIdCardFrontPic());
return null;
});
LegalPersonInfoDto legalPersonInfo = reqDto.getLegalPersonInfo();
// 法人身份证反面
tasks.add(() -> {
uploadImageToThird(legalPersonInfo.getIdCardBackPic());
return null;
});
// 法人身份证正面
tasks.add(() -> {
uploadImageToThird(legalPersonInfo.getIdCardFrontPic());
return null;
});
// 法人手持身份证
tasks.add(() -> {
uploadImageToThird(legalPersonInfo.getIdCardHandPic());
return null;
});
BusinessLicenceInfoDto businessLicenceInfo = reqDto.getBusinessLicenceInfo();
// 营业执照
tasks.add(() -> {
uploadImageToThird(businessLicenceInfo.getLicensePic());
return null;
});
SettlementInfoDto settlementInfo = reqDto.getSettlementInfo();
// 银行卡背面
tasks.add(() -> {
uploadImageToThird(settlementInfo.getBankCardBackPic());
return null;
});
// 银行卡正面
tasks.add(() -> {
uploadImageToThird(settlementInfo.getBankCardFrontPic());
return null;
});
// 开户许可证
tasks.add(() -> {
uploadImageToThird(settlementInfo.getOpenAccountLicencePic());
return null;
});
// 非法人手持授权函
tasks.add(() -> {
uploadImageToThird(settlementInfo.getNoLegalHandSettleAuthPic());
return null;
});
// 非法人授权函
tasks.add(() -> {
uploadImageToThird(settlementInfo.getNoLegalSettleAuthPic());
return null;
});
// 非法人身份证反面
tasks.add(() -> {
uploadImageToThird(settlementInfo.getNoLegalIdCardBackPic());
return null;
});
// 非法人身份证正面
tasks.add(() -> {
uploadImageToThird(settlementInfo.getNoLegalIdCardFrontPic());
return null;
});
StoreInfoDto storeInfo = reqDto.getStoreInfo();
// 店内图片
tasks.add(() -> {
uploadImageToThird(storeInfo.getInsidePic());
return null;
});
// 门店门头图片
tasks.add(() -> {
uploadImageToThird(storeInfo.getDoorPic());
return null;
});
// 收银台图片
tasks.add(() -> {
uploadImageToThird(storeInfo.getCashierDeskPic());
return null;
});
// 执行所有任务
AsyncTaskExecutor.executeAll(tasks);
}
private static void uploadImageToThird(ImageDto dto) {
if (dto != null && StrUtil.isNotBlank(dto.getUrl())) {
if (StrUtil.isBlank(dto.getWechatId())) {
String image = WechatEntryManager.uploadImage(null, dto.getUrl());
dto.setWechatId(image);
}
if (StrUtil.isBlank(dto.getAlipayId())) {
String image = AlipayEntryManager.uploadImage(null, dto.getUrl());
dto.setAlipayId(image);
}
}
}
/**
* 验证进件参数
*
* @param reqDto 进件参数
*/
public static void verifyEntryParam(AggregateMerchantDto reqDto) {
AssertUtil.isBlank(reqDto.getMerchantCode(), "商户编号不能为空");
AssertUtil.isNull(reqDto.getMerchantBaseInfo(), "商户基础信息不能为空");
MerchantBaseInfoDto baseInfo = reqDto.getMerchantBaseInfo();
AssertUtil.isBlank(baseInfo.getUserType(), "商户类型不能为空");
AssertUtil.isBlank(baseInfo.getShortName(), "商户简称不能为空");
AssertUtil.isBlank(baseInfo.getMccCode(), "商户行业编码不能为空");
AssertUtil.isBlank(baseInfo.getContactPersonType(), "联系人类型不能为空");
if (!PayCst.ContactPersonType.SUPER.equals(baseInfo.getContactPersonType()) && !PayCst.ContactPersonType.LEGAL.equals(baseInfo.getContactPersonType())) {
throw new CzgException("联系人类型错误");
}
if (PayCst.ContactPersonType.SUPER.equals(baseInfo.getContactPersonType())) {
AssertUtil.isBlank(baseInfo.getCertType(), "证件类型不能为空");
if (!"0".equals(baseInfo.getCertType())) {
throw new CzgException("证件类型错误");
}
AssertUtil.isBlank(baseInfo.getContactName(), "联系人姓名不能为空");
AssertUtil.isBlank(baseInfo.getContactPersonId(), "联系人身份证号不能为空");
AssertUtil.isBlank(baseInfo.getContactPhone(), "联系人电话不能为空");
AssertUtil.isBlank(baseInfo.getContactAddr(), "联系人地址不能为空");
AssertUtil.isBlank(baseInfo.getContactEmail(), "联系人邮箱不能为空");
AssertUtil.isBlank(baseInfo.getContactPersonIdStartDate(), "联系人身份证开始日期不能为空");
AssertUtil.isBlank(baseInfo.getContactPersonIdEndDate(), "联系人身份证到期日期不能为空");
AssertUtil.isNull(baseInfo.getContactIdCardBackPic(), "联系人身份证反面不能为空");
AssertUtil.isBlank(baseInfo.getContactIdCardBackPic().getUrl(), "联系人身份证反面不能为空");
AssertUtil.isNull(baseInfo.getContactIdCardFrontPic(), "联系人身份证正面不能为空");
AssertUtil.isBlank(baseInfo.getContactIdCardFrontPic().getUrl(), "联系人身份证正面不能为空");
}
AssertUtil.isBlank(baseInfo.getCompanyChildType(), "商户类型不能为空");
if ("0".equals(baseInfo.getUserType())) {
// 个体商户, 暂无其他校验
} else if ("1".equals(baseInfo.getUserType())) {
if (!"1".equals(baseInfo.getCompanyChildType()) && !"2".equals(baseInfo.getCompanyChildType()) && !"3".equals(baseInfo.getCompanyChildType())) {
throw new CzgException("商户类型错误");
}
} else {
throw new CzgException("商户类型错误");
}
AssertUtil.isNull(reqDto.getBusinessLicenceInfo(), "营业执照信息不能为空");
BusinessLicenceInfoDto businessLicenceInfo = reqDto.getBusinessLicenceInfo();
AssertUtil.isBlank(businessLicenceInfo.getLicenceNo(), "营业执照编号不能为空");
AssertUtil.isBlank(businessLicenceInfo.getLicenceName(), "营业执照名称不能为空");
AssertUtil.isBlank(businessLicenceInfo.getRegisteredAddress(), "营业执照注册地址不能为空");
AssertUtil.isBlank(businessLicenceInfo.getLicenceStartDate(), "营业执照开始日期不能为空");
AssertUtil.isBlank(businessLicenceInfo.getLicenceEndDate(), "营业执照到期日期不能为空");
AssertUtil.isNull(businessLicenceInfo.getLicensePic(), "营业执照名称不能为空");
AssertUtil.isBlank(businessLicenceInfo.getLicensePic().getUrl(), "营业执照名称不能为空");
AssertUtil.isNull(reqDto.getLegalPersonInfo(), "法人信息不能为空");
LegalPersonInfoDto legalPersonInfo = reqDto.getLegalPersonInfo();
AssertUtil.isBlank(legalPersonInfo.getLegalPersonName(), "法人姓名不能为空");
AssertUtil.isBlank(legalPersonInfo.getLegalPersonId(), "法人身份证号不能为空");
AssertUtil.isBlank(legalPersonInfo.getLegalIdPersonStartDate(), "法人身份证开始日期不能为空");
AssertUtil.isBlank(legalPersonInfo.getLegalPersonIdEndDate(), "法人身份证到期日期不能为空");
AssertUtil.isBlank(legalPersonInfo.getLegalPersonPhone(), "法人电话不能为空");
AssertUtil.isBlank(legalPersonInfo.getLegalPersonEmail(), "法人邮箱不能为空");
AssertUtil.isBlank(legalPersonInfo.getLegalGender(), "法人性别不能为空");
AssertUtil.isBlank(legalPersonInfo.getLegalAddress(), "法人地址不能为空");
AssertUtil.isNull(legalPersonInfo.getIdCardHandPic(), "法人身份证手持不能为空");
AssertUtil.isBlank(legalPersonInfo.getIdCardHandPic().getUrl(), "法人身份证手持不能为空");
AssertUtil.isNull(legalPersonInfo.getIdCardFrontPic(), "法人身份证正面不能为空");
AssertUtil.isBlank(legalPersonInfo.getIdCardFrontPic().getUrl(), "法人身份证正面不能为空");
AssertUtil.isNull(legalPersonInfo.getIdCardBackPic(), "法人身份证反面不能为空");
AssertUtil.isBlank(legalPersonInfo.getIdCardBackPic().getUrl(), "法人身份证反面不能为空");
AssertUtil.isNull(reqDto.getStoreInfo(), "门店信息不能为空");
StoreInfoDto storeInfo = reqDto.getStoreInfo();
AssertUtil.isBlank(storeInfo.getMercProvCode(), "门店省ID不能为空");
AssertUtil.isBlank(storeInfo.getMercCityCode(), "门店市ID不能为空");
AssertUtil.isBlank(storeInfo.getMercAreaCode(), "门店区ID不能为空");
AssertUtil.isBlank(storeInfo.getMercProv(), "门店省不能为空");
AssertUtil.isBlank(storeInfo.getMercCity(), "门店市不能为空");
AssertUtil.isBlank(storeInfo.getMercArea(), "门店区不能为空");
AssertUtil.isBlank(storeInfo.getBusinessAddress(), "门店营业地址不能为空");
AssertUtil.isNull(storeInfo.getInsidePic(), "门店营业地址不能为空");
AssertUtil.isBlank(storeInfo.getInsidePic().getUrl(), "门店营业地址不能为空");
AssertUtil.isNull(storeInfo.getDoorPic(), "门店门头照不能为空");
AssertUtil.isBlank(storeInfo.getDoorPic().getUrl(), "门店门头照不能为空");
AssertUtil.isNull(storeInfo.getCashierDeskPic(), "门店收银台照不能为空");
AssertUtil.isBlank(storeInfo.getCashierDeskPic().getUrl(), "门店收银台照不能为空");
AssertUtil.isNull(reqDto.getSettlementInfo(), "结算信息不能为空");
SettlementInfoDto settlementInfo = reqDto.getSettlementInfo();
AssertUtil.isBlank(settlementInfo.getSettlementCardNo(), "结算卡号不能为空");
AssertUtil.isBlank(settlementInfo.getSettlementName(), "结算账户户名不能为空");
AssertUtil.isBlank(settlementInfo.getBankName(), "结算银行名称不能为空");
AssertUtil.isBlank(settlementInfo.getBankInstId(), "结算银行缩写不能为空");
AssertUtil.isBlank(settlementInfo.getBankMobile(), "结算银行预留手机号不能为空");
AssertUtil.isBlank(settlementInfo.getSettlementCardType(), "结算卡类型不能为空");
AssertUtil.isBlank(settlementInfo.getOpenAccProvinceId(), "结算开户行省ID不能为空");
AssertUtil.isBlank(settlementInfo.getOpenAccCityId(), "结算开户行市ID不能为空");
AssertUtil.isBlank(settlementInfo.getOpenAccAreaId(), "结算开户行区ID不能为空");
AssertUtil.isBlank(settlementInfo.getOpenAccProvince(), "结算开户行省不能为空");
AssertUtil.isBlank(settlementInfo.getOpenAccCity(), "结算开户行市不能为空");
AssertUtil.isBlank(settlementInfo.getOpenAccArea(), "结算开户行区不能为空");
if ("11".equals(settlementInfo.getSettlementCardType())) {
// 11 对私借记卡(结算卡正面照、结算卡反面照图片必传)
AssertUtil.isNull(settlementInfo.getBankCardFrontPic(), "结算卡正面照不能为空");
AssertUtil.isBlank(settlementInfo.getBankCardFrontPic().getUrl(), "结算卡正面照不能为空");
AssertUtil.isNull(settlementInfo.getBankCardBackPic(), "结算卡反面照不能为空");
AssertUtil.isBlank(settlementInfo.getBankCardBackPic().getUrl(), "结算卡反面照不能为空");
AssertUtil.isBlank(settlementInfo.getSettlementType(), "结算类型不能为空");
if ("0".equals(settlementInfo.getSettlementType())) {
// 非法人结算
AssertUtil.isBlank(settlementInfo.getNoLegalName(), "非法人姓名不能为空");
AssertUtil.isBlank(settlementInfo.getNoLegalId(), "非法人身份证号不能为空");
AssertUtil.isNull(settlementInfo.getNoLegalIdCardFrontPic(), "非法人身份证正面不能为空");
AssertUtil.isBlank(settlementInfo.getNoLegalIdCardFrontPic().getUrl(), "非法人身份证正面不能为空");
AssertUtil.isNull(settlementInfo.getNoLegalIdCardBackPic(), "非法人身份证反面不能为空");
AssertUtil.isBlank(settlementInfo.getNoLegalIdCardBackPic().getUrl(), "非法人身份证反面不能为空");
AssertUtil.isNull(settlementInfo.getNoLegalSettleAuthPic(), "非法人结算授权书不能为空");
AssertUtil.isBlank(settlementInfo.getNoLegalSettleAuthPic().getUrl(), "非法人结算授权书不能为空");
} else if ("1".equals(settlementInfo.getSettlementType())) {
// 法人结算 暂无处理
} else {
throw new CzgException("结算类型错误");
}
} else if ("21".equals(settlementInfo.getSettlementCardType())) {
// 21 对公借记卡(只须结算卡正面照片)
AssertUtil.isNull(settlementInfo.getOpenAccountLicencePic(), "开户许可证不能为空");
AssertUtil.isBlank(settlementInfo.getOpenAccountLicencePic().getUrl(), "开户许可证不能为空");
} else {
throw new CzgException("结算卡类型错误");
}
}
public static void main(String[] args) {
AggregateMerchantDto merchantDto = getTestMerchantEntryData();
// verifyEntryParam(merchantDto);
// uploadParamImage(merchantDto);
verifyEntryParam(merchantDto);
uploadParamImage(merchantDto);
System.out.println(merchantDto);
// entryMerchant(merchantDto, PayCst.Platform.WECHAT);
// entryMerchant(merchantDto, PayCst.Platform.ALIPAY);
entryMerchant(merchantDto, PayCst.Platform.WECHAT, PayCst.Platform.ALIPAY);
}
private static AggregateMerchantDto getTestMerchantEntryData() {
AggregateMerchantDto merchantDto = new AggregateMerchantDto();
merchantDto.setMerchantCode("20220106000000000001");
MerchantBaseInfoDto baseInfoDto = new MerchantBaseInfoDto();
baseInfoDto.setUserType("1");
baseInfoDto.setCompanyChildType("1");
baseInfoDto.setShortName("测试商户");
baseInfoDto.setMccCode("0741");
baseInfoDto.setContactPersonType(PayCst.ContactPersonType.SUPER);
baseInfoDto.setContactName("张三");
baseInfoDto.setContactPersonId("110101199001011234");
baseInfoDto.setContactPersonIdStartDate("2021-01-01");
baseInfoDto.setContactPersonIdEndDate("2025-01-01");
baseInfoDto.setContactPhone("13800000000");
baseInfoDto.setContactEmail("1157756119@qq.com");
baseInfoDto.setContactAddr("广东省深圳市南山区");
baseInfoDto.setContactIdCardFrontPic(new ImageDto("https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/20240312/31476c871c224389aea0ac4e17c964a3.jpg"));
baseInfoDto.setContactIdCardBackPic(new ImageDto("https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/20240312/31476c871c224389aea0ac4e17c964a3.jpg"));
merchantDto.setMerchantBaseInfo(baseInfoDto);
BusinessLicenceInfoDto businessLicenceInfoDto = new BusinessLicenceInfoDto();
businessLicenceInfoDto.setLicenceName("测试商户");
businessLicenceInfoDto.setLicenceNo("110101199001011234");
businessLicenceInfoDto.setLicenceStartDate("2021-01-01");
businessLicenceInfoDto.setLicenceEndDate("2052-01-01");
businessLicenceInfoDto.setRegisteredAddress("广东省深圳市南山区");
businessLicenceInfoDto.setLicensePic(new ImageDto("https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/20240312/31476c871c224389aea0ac4e17c964a3.jpg"));
merchantDto.setBusinessLicenceInfo(businessLicenceInfoDto);
LegalPersonInfoDto legalPersonInfoDto = new LegalPersonInfoDto();
legalPersonInfoDto.setLegalPersonName("张三");
legalPersonInfoDto.setLegalPersonId("110101199001011234");
legalPersonInfoDto.setLegalIdPersonStartDate("2021-01-01");
legalPersonInfoDto.setLegalPersonIdEndDate("2055-01-01");
legalPersonInfoDto.setLegalPersonPhone("13800000000");
legalPersonInfoDto.setLegalPersonEmail("1157756119@qq.com");
legalPersonInfoDto.setLegalGender("1");
legalPersonInfoDto.setLegalAddress("广东省深圳市南山区");
legalPersonInfoDto.setIdCardHandPic(new ImageDto("https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/20240312/31476c871c224389aea0ac4e17c964a3.jpg"));
legalPersonInfoDto.setIdCardFrontPic(new ImageDto("https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/20240312/31476c871c224389aea0ac4e17c964a3.jpg"));
legalPersonInfoDto.setIdCardBackPic(new ImageDto("https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/20240312/31476c871c224389aea0ac4e17c964a3.jpg"));
merchantDto.setLegalPersonInfo(legalPersonInfoDto);
StoreInfoDto storeInfoDto = new StoreInfoDto();
storeInfoDto.setMercProvCode("440000");
storeInfoDto.setMercCityCode("440300");
storeInfoDto.setMercAreaCode("440303");
storeInfoDto.setMercProv("广东省");
storeInfoDto.setMercCity("深圳市");
storeInfoDto.setMercArea("南山区");
storeInfoDto.setBusinessAddress("广东省深圳市南山区");
storeInfoDto.setInsidePic(new ImageDto("https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/20240312/31476c871c224389aea0ac4e17c964a3.jpg"));
storeInfoDto.setDoorPic(new ImageDto("https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/20240312/31476c871c224389aea0ac4e17c964a3.jpg"));
storeInfoDto.setCashierDeskPic(new ImageDto("https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/20240312/31476c871c224389aea0ac4e17c964a3.jpg"));
merchantDto.setStoreInfo(storeInfoDto);
SettlementInfoDto settlementInfoDto = new SettlementInfoDto();
settlementInfoDto.setSettlementType("1");
settlementInfoDto.setSettlementCardType("21");
settlementInfoDto.setSettlementName("张三");
settlementInfoDto.setSettlementCardNo("110101199001011234");
settlementInfoDto.setBankMobile("13800000000");
settlementInfoDto.setOpenAccProvinceId("440000");
settlementInfoDto.setOpenAccCityId("440300");
settlementInfoDto.setOpenAccAreaId("440303");
settlementInfoDto.setOpenAccProvince("广东省");
settlementInfoDto.setOpenAccCity("深圳市");
settlementInfoDto.setOpenAccArea("南山区");
settlementInfoDto.setBankName("中国工商银行");
settlementInfoDto.setBankInstId("ICBC");
settlementInfoDto.setBankType("1");
settlementInfoDto.setBankBranchName("广东省深圳市南山区");
settlementInfoDto.setBankBranchCode("440300");
settlementInfoDto.setBankCardFrontPic(new ImageDto("https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/20240312/31476c871c224389aea0ac4e17c964a3.jpg"));
settlementInfoDto.setBankCardBackPic(new ImageDto("https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/20240312/31476c871c224389aea0ac4e17c964a3.jpg"));
settlementInfoDto.setOpenAccountLicencePic(new ImageDto("https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/20240312/31476c871c224389aea0ac4e17c964a3.jpg"));
merchantDto.setSettlementInfo(settlementInfoDto);
return merchantDto;
}
}

View File

@@ -0,0 +1,82 @@
package com.czg;
import java.util.List;
/**
* 支付相关常量
*
* @author yjjie
* @date 2026/1/7 09:20
*/
public interface PayCst {
/**
* 平台
*/
class Platform {
/**
* 微信
*/
public static final String WECHAT = "wechat";
/**
* 支付宝
*/
public static final String ALIPAY = "alipay";
}
/**
* 进件状态
*/
class EntryStatus {
/**
* 待提交
*/
public static final String WAIT = "WAIT";
/**
* 待处理
*/
public static final String INIT = "INIT";
/**
* 审核中
*/
public static final String AUDIT = "AUDIT";
/**
* 待签约
*/
public static final String SIGN = "SIGN";
/**
* 已完成
*/
public static final String FINISH = "FINISH";
/**
* 失败
*/
public static final String REJECTED = "REJECTED";
/**
* 需要查询的状态列表
* 待处理、待签约、待审核的进件
*/
public static final List<String> NEED_QUERY_LIST = List.of(INIT, AUDIT, SIGN);
}
/**
* 联系人类型
*/
class ContactPersonType {
/**
* 法人
*/
public static final String LEGAL = "LEGAL";
/**
* 经办人
*/
public static final String SUPER = "SUPER";
}
}

View File

@@ -0,0 +1,54 @@
package com.czg.dto.req;
import lombok.Data;
/**
* 统一进件
*
* @author yjjie
* @date 2026/1/6 10:02
*/
@Data
public class AggregateMerchantDto {
/**
* 商户Id
*/
private Long shopId;
/**
* 【必填】
* 自己生成 CZGyyyy-MM-dd HH:mm:ss.SSS
* 商户编号(在当前系统唯一)
*/
private String merchantCode;
/**
* 【必填】
* 商户基础信息
*/
private MerchantBaseInfoDto merchantBaseInfo;
/**
* 【必填】
* 法人信息
*/
private LegalPersonInfoDto legalPersonInfo;
/**
* 【必填】
* 营业执照信息
*/
private BusinessLicenceInfoDto businessLicenceInfo;
/**
* 【必填】
* 门店信息
*/
private StoreInfoDto storeInfo;
/**
* 【必填】
* 结算信息
*/
private SettlementInfoDto settlementInfo;
}

View File

@@ -0,0 +1,42 @@
package com.czg.dto.req;
import lombok.Data;
/**
* 营业执照信息
* @author yjjie
* @date 2026/1/6 10:17
*/
@Data
public class BusinessLicenceInfoDto {
/**
* 营业执照全称--非小微必填
*/
private String licenceName;
/**
* 营业执照号码--非小微必填
*/
private String licenceNo;
/**
* 营业执照开始日期--非小微必填
*/
private String licenceStartDate;
/**
* 营业执照结束日期--非小微必填
*/
private String licenceEndDate;
/**
* 营业执照注册地址--非小微必填
*/
private String registeredAddress;
/**
* 营业执照
*/
private ImageDto licensePic;
}

View File

@@ -0,0 +1,34 @@
package com.czg.dto.req;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @author yjjie
* @date 2026/1/6 16:53
*/
@Data
@Accessors(chain = true)
public class ImageDto {
/**
* 图片 url
*/
private String url;
/**
* 微信 图片 mediaId
*/
private String wechatId;
/**
* 支付宝 图片 mediaId
*/
private String alipayId;
public ImageDto() {}
public ImageDto(String url) {
this.url = url;
}
}

View File

@@ -0,0 +1,76 @@
package com.czg.dto.req;
import lombok.Data;
/**
* 法人信息
* @author yjjie
* @date 2026/1/6 10:13
*/
@Data
public class LegalPersonInfoDto {
/**
* 【必填】
* 法人姓名
*/
private String legalPersonName;
/**
* 【必填】
* 法人身份证号
*/
private String legalPersonId;
/**
* 【必填】
* 法人身份证开始日期
*/
private String legalIdPersonStartDate;
/**
* 【必填】
* 法人身份证到期日期
*/
private String legalPersonIdEndDate;
/**
* 【必填】
* 法人电话
*/
private String legalPersonPhone;
/**
* 【必填】
* 法人邮箱
*/
private String legalPersonEmail;
/**
* 法人性别0男 1女
*/
private String legalGender;
/**
* 【必填】
* 法人地址
*/
private String legalAddress;
/**
* 身份证手持 图片
*/
private ImageDto idCardHandPic;
/**
* 【必填】
* 身份证正面
*/
private ImageDto idCardFrontPic;
/**
* 【必填】
* 身份证反面
*/
private ImageDto idCardBackPic;
}

View File

@@ -0,0 +1,97 @@
package com.czg.dto.req;
import lombok.Data;
/**
* 商户基础信息
*
* @author yjjie
* @date 2026/1/6 10:09
*/
@Data
public class MerchantBaseInfoDto {
/**
* 【必填】
* 商户类型
* 0: 个体商户;
* 1: 企业商户;
* 3: 小微商户 暂不支持
*/
private String userType;
/**
* 【必填】
* 商户简称--企业、个体必填
*/
private String shortName;
/**
* 【必填】
* 行业编码
*/
private String mccCode;
/**
* 联系人类型
* LEGAL: 经营者/法定代表人
* SUPER: 经办人
*/
private String contactPersonType = "LEGAL";
/**
* 联系人姓名
*/
private String contactName;
/**
* 证件类型
* 目前只支持身份证 传值0
*/
private String certType = "0";
/**
* 联系人身份证号
*/
private String contactPersonId;
/**
* 联系人身份证开始日期
*/
private String contactPersonIdStartDate;
/**
* 联系人身份证到期日期
*/
private String contactPersonIdEndDate;
/**
* 联系人身份证背面
*/
private ImageDto contactIdCardBackPic;
/**
* 联系人身份证正面
*/
private ImageDto contactIdCardFrontPic;
/**
* 联系人电话
*/
private String contactPhone;
/**
* 联系人通讯地址
*/
private String contactAddr;
/**
* 联系人邮箱
*/
private String contactEmail;
/**
* 企业类型1普通企业2事业单位3政府机关4社会组织
*/
private String companyChildType = "1";
}

View File

@@ -0,0 +1,138 @@
package com.czg.dto.req;
import lombok.Data;
/**
* 结算信息
*
* @author yjjie
* @date 2026/1/6 10:22
*/
@Data
public class SettlementInfoDto {
/**
* 结算类型 0:非法人结算, 1:法人结算
*/
private String settlementType;
/**
* 非法人姓名
*/
private String noLegalName;
/**
* 非法人身份证号码
*/
private String noLegalId;
/**
* 结算卡类型 必填 11 对私借记卡(结算卡正面照、结算卡反面照图片必传) 21 对公借记卡(只须结算卡正面照片)
*/
private String settlementCardType;
/**
* 结算账户卡号
*/
private String settlementCardNo;
/**
* 结算账户户名
*/
private String settlementName;
/**
* 结算银行预留手机号
*/
private String bankMobile;
/**
* 开户行省ID
*/
private String openAccProvinceId;
/**
* 开户行市ID
*/
private String openAccCityId;
/**
* 开户行区ID
*/
private String openAccAreaId;
/**
* 开户行省
*/
private String openAccProvince;
/**
* 开户行市
*/
private String openAccCity;
/**
* 开户行区
*/
private String openAccArea;
/**
* 开户行行别名称
*/
private String bankName;
/**
* 开户行缩写
*/
private String bankInstId;
/**
* 开户行编号
*/
private String bankType;
/**
* 支行开户行行别名称
*/
private String bankBranchName;
/**
* 支行开户行编号
*/
private String bankBranchCode;
/**
* 银行卡正面
*/
private ImageDto bankCardFrontPic;
/**
* 银行卡反面
*/
private ImageDto bankCardBackPic;
/**
* 开户许可证
*/
private ImageDto openAccountLicencePic;
/**
* 非法人手持结算授权书
*/
private ImageDto noLegalHandSettleAuthPic;
/**
* 非法人结算授权书
*/
private ImageDto noLegalSettleAuthPic;
/**
* 非法人身份证正面
*/
private ImageDto noLegalIdCardFrontPic;
/**
* 非法人身份证反面
*/
private ImageDto noLegalIdCardBackPic;
}

View File

@@ -0,0 +1,66 @@
package com.czg.dto.req;
import lombok.Data;
/**
* 门店信息
* @author yjjie
* @date 2026/1/6 10:19
*/
@Data
public class StoreInfoDto {
/**
* 【必填】
* 商户归属省Code
*/
private String mercProvCode;
/**
* 【必填】
* 商户归属市Code
*/
private String mercCityCode;
/**
* 【必填】
* 商户归属区Code
*/
private String mercAreaCode;
/**
* 商户归属省
*/
private String mercProv;
/**
* 商户归属市
*/
private String mercCity;
/**
* 商户归属区
*/
private String mercArea;
/**
* 【必填】
* 营业地址
*/
private String businessAddress;
/**
* 经营场所内设照片
*/
private ImageDto insidePic;
/**
* 门头照
*/
private ImageDto doorPic;
/**
* 收银台照片
*/
private ImageDto cashierDeskPic;
}

View File

@@ -0,0 +1,58 @@
package com.czg.dto.resp;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* 银行支行信息
* @author yjjie
* @date 2026/1/6 15:37
*/
@Data
public class BankBranchDto {
/**
* 机构ID
*/
private String instId;
/**
* 银行代码
*/
private String bankCode;
/**
* 城市
*/
private String city;
/**
* 银行简称
*/
private String simpleName;
/**
* 省份
*/
@JSONField(name = "provice")
private String province;
/**
* IBPS代码
*/
private String ibpsCode;
/**
* 承担机构
*/
private String undertakeInst;
/**
* 分支机构名称
*/
private String branchName;
/**
* 机构全称
*/
private String instName;
}

View File

@@ -0,0 +1,46 @@
package com.czg.dto.resp;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 进件相应结果
* @author yjjie
* @date 2026/1/6 18:15
*/
@Data
@Accessors(chain = true)
public class EntryRespDto {
/**
* 微信申请 Id
*/
private String wechatApplyId;
/**
* 微信状态
* {@link com.czg.PayCst.EntryStatus}
*/
private String wechatStatus;
/**
* 微信进件错误信息
*/
private String wechatErrorMsg;
/**
* 支付宝订单 Id
*/
private String alipayOrderId;
/**
* 支付宝状态
* {@link com.czg.PayCst.EntryStatus}
*/
private String alipayStatus;
/**
* 支付宝进件错误信息
*/
private String alipayErrorMsg;
}

View File

@@ -0,0 +1,34 @@
package com.czg.dto.resp;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @author yjjie
* @date 2026/1/7 09:07
*/
@Data
@Accessors(chain = true)
public class EntryThirdRespDto {
/**
* 录入id
*/
private String entryId;
/**
* 平台
*/
private String platform;
/**
* 状态
* {@link com.czg.PayCst.EntryStatus}
*/
private String status;
/**
* 错误信息
*/
private String errorMsg;
}

View File

@@ -0,0 +1,49 @@
package com.czg.dto.resp;
import lombok.Data;
/**
* 查询进件状态
* @author yjjie
* @date 2026/1/7 17:12
*/
@Data
public class QueryStatusResp {
/**
* 平台
* {@link com.czg.PayCst.Platform}
*/
private String platform;
/**
* 商户编号
*/
private String merchantCode;
/**
* 进件编号
*/
private String applyId;
/**
* 三方商户id
*/
private String thirdMerchantId;
/**
* 进件状态
* {@link com.czg.PayCst.EntryStatus}
*/
private String status;
/**
* 签约地址
*/
private String signUrl;
/**
* 失败原因
*/
private String failReason;
}

View File

@@ -0,0 +1,61 @@
package com.czg.third.alipay;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.v3.ApiClient;
import com.alipay.v3.Configuration;
import com.czg.third.alipay.dto.config.AlipayConfigDto;
import lombok.extern.slf4j.Slf4j;
/**
* @author yjjie
* @date 2026/1/4 13:48
*/
@Slf4j
public class AlipayClient {
public static DefaultAlipayClient getDefaultClient(AlipayConfigDto configDto) {
try {
return new DefaultAlipayClient(getAlipayConfig(configDto));
} catch (AlipayApiException e) {
log.error("创建支付宝客户端失败", e);
return null;
}
}
public static void setApiClient(AlipayConfigDto configDto) {
if (configDto == null) {
configDto = AlipayConfigDto.getDefaultConfig();
}
try {
ApiClient defaultClient = Configuration.getDefaultApiClient();
// 初始化alipay参数全局设置一次
com.alipay.v3.util.model.AlipayConfig alipayConfig = new com.alipay.v3.util.model.AlipayConfig();
alipayConfig.setServerUrl(configDto.getDomain());
alipayConfig.setAppId(configDto.getAppId());
alipayConfig.setAlipayPublicKey(configDto.getAlipayPublicKey());
alipayConfig.setPrivateKey(configDto.getPrivateKey());
defaultClient.setAlipayConfig(alipayConfig);
} catch (Exception e) {
log.error("创建支付宝客户端失败", e);
}
}
public static AlipayConfig getAlipayConfig(AlipayConfigDto configDto) {
if (configDto == null) {
configDto = AlipayConfigDto.getDefaultConfig();
}
AlipayConfig alipayConfig = new AlipayConfig();
alipayConfig.setServerUrl(configDto.getDomain());
alipayConfig.setAppId(configDto.getAppId());
alipayConfig.setPrivateKey(configDto.getPrivateKey());
alipayConfig.setFormat("json");
alipayConfig.setAlipayPublicKey(configDto.getAlipayPublicKey());
alipayConfig.setCharset("UTF-8");
alipayConfig.setSignType("RSA2");
return alipayConfig;
}
}

View File

@@ -0,0 +1,382 @@
package com.czg.third.alipay;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alipay.api.AlipayApiException;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.FileItem;
import com.alipay.api.domain.*;
import com.alipay.api.request.AlipayFinancialnetAuthPbcnameQueryRequest;
import com.alipay.api.request.AntMerchantExpandIndirectImageUploadRequest;
import com.alipay.api.request.AntMerchantExpandIndirectZftCreateRequest;
import com.alipay.api.request.AntMerchantExpandIndirectZftorderQueryRequest;
import com.alipay.api.response.AlipayFinancialnetAuthPbcnameQueryResponse;
import com.alipay.api.response.AntMerchantExpandIndirectImageUploadResponse;
import com.alipay.api.response.AntMerchantExpandIndirectZftCreateResponse;
import com.alipay.api.response.AntMerchantExpandIndirectZftorderQueryResponse;
import com.alipay.v3.ApiClient;
import com.alipay.v3.ApiException;
import com.alipay.v3.Configuration;
import com.alipay.v3.api.AlipayOpenAgentApi;
import com.alipay.v3.model.AlipayOpenAgentCreateDefaultResponse;
import com.alipay.v3.model.AlipayOpenAgentCreateModel;
import com.alipay.v3.model.AlipayOpenAgentCreateResponseModel;
import com.alipay.v3.model.ContactModel;
import com.alipay.v3.util.model.AlipayConfig;
import com.czg.PayCst;
import com.czg.dto.req.*;
import com.czg.dto.resp.BankBranchDto;
import com.czg.dto.resp.EntryThirdRespDto;
import com.czg.dto.resp.QueryStatusResp;
import com.czg.exception.CzgException;
import com.czg.third.alipay.dto.config.AlipayConfigDto;
import com.czg.third.wechat.dto.resp.WechatQueryStateResp;
import com.czg.utils.UploadFileUtil;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
/**
* 支付宝进件管理
* <a href="https://opendocs.alipay.com/solution/9434dd99_ant.merchant.expand.indirect.zft.simplecreate?scene=bf5951260023430e944c2e9afdf7f9e2&pathHash=d3136936">...</a>
*
* @author yjjie
* @date 2025/12/29 14:11
*/
@Slf4j
public class AlipayEntryManager {
/**
* 查询商户进件状态
*
* @param configDto 配置信息
* @param merchantCode 自系统的商户编号
* @return 进件状态
*/
public static QueryStatusResp queryMerchantEntryStatus(AlipayConfigDto configDto, String merchantCode) {
QueryStatusResp queryStatusResp = new QueryStatusResp();
queryStatusResp.setPlatform(PayCst.Platform.ALIPAY);
queryStatusResp.setMerchantCode(merchantCode);
AntMerchantExpandIndirectZftorderQueryRequest request = new AntMerchantExpandIndirectZftorderQueryRequest();
AntMerchantExpandIndirectZftorderQueryModel model = new AntMerchantExpandIndirectZftorderQueryModel();
model.setExternalId(merchantCode);
request.setBizModel(model);
try {
DefaultAlipayClient defaultAlipayClient = AlipayClient.getDefaultClient(configDto);
AntMerchantExpandIndirectZftorderQueryResponse response = defaultAlipayClient.execute(request);
log.info("支付宝查询进件状态结果:{}", response.getBody());
if (response.isSuccess()) {
List<ZftSubMerchantOrder> orders = response.getOrders();
if (orders != null && !orders.isEmpty()) {
ZftSubMerchantOrder first = orders.getFirst();
queryStatusResp.setApplyId(first.getExternalId());
if ("99".equals(first.getStatus())) {
// 已完成
queryStatusResp.setStatus(PayCst.EntryStatus.FINISH);
queryStatusResp.setThirdMerchantId(first.getSmid());
queryStatusResp.setFailReason("");
return queryStatusResp;
}
if ("-1".equals(first.getStatus())) {
// 拒绝
queryStatusResp.setStatus(PayCst.EntryStatus.REJECTED);
queryStatusResp.setFailReason(first.getReason());
return queryStatusResp;
}
if (StrUtil.isNotBlank(first.getFkAudit())) {
if ("REJECT".equals(first.getFkAudit())) {
// 风控拒绝
queryStatusResp.setStatus(PayCst.EntryStatus.REJECTED);
queryStatusResp.setFailReason(first.getFkAuditMemo());
return queryStatusResp;
}
if ("CREATE".equals(first.getFkAudit())) {
queryStatusResp.setStatus(PayCst.EntryStatus.AUDIT);
queryStatusResp.setFailReason(first.getFkAuditMemo());
return queryStatusResp;
}
}
if (StrUtil.isNotBlank(first.getKzAudit())) {
if ("REJECT".equals(first.getKzAudit())) {
// 风控拒绝
queryStatusResp.setStatus(PayCst.EntryStatus.REJECTED);
queryStatusResp.setFailReason(first.getKzAuditMemo());
return queryStatusResp;
}
if ("CREATE".equals(first.getKzAudit())) {
queryStatusResp.setStatus(PayCst.EntryStatus.AUDIT);
queryStatusResp.setFailReason(first.getKzAuditMemo());
return queryStatusResp;
}
}
if (StrUtil.isNotBlank(first.getSubConfirm()) && "FAIL".equals(first.getSubConfirm())) {
// 签约失败
queryStatusResp.setStatus(PayCst.EntryStatus.REJECTED);
queryStatusResp.setFailReason("签约失败");
return queryStatusResp;
}
queryStatusResp.setStatus(PayCst.EntryStatus.SIGN);
queryStatusResp.setSignUrl(first.getSubSignQrCodeUrl());
return queryStatusResp;
}
} else {
queryStatusResp.setStatus(PayCst.EntryStatus.REJECTED);
queryStatusResp.setFailReason(response.getSubMsg());
}
} catch (AlipayApiException e) {
log.error("支付宝查询进件状态报错:{}", e.getMessage(), e);
queryStatusResp.setStatus(PayCst.EntryStatus.INIT);
}
return queryStatusResp;
}
/**
* 进件商户
*
* @param configDto 配置信息
* @param reqDto 请求信息
*/
public static EntryThirdRespDto entryMerchant(AlipayConfigDto configDto, AggregateMerchantDto reqDto) {
AntMerchantExpandIndirectZftCreateModel entryReqDto = buildEntryParams(reqDto);
EntryThirdRespDto respDto = new EntryThirdRespDto()
.setPlatform(PayCst.Platform.ALIPAY);
try {
AntMerchantExpandIndirectZftCreateRequest request = new AntMerchantExpandIndirectZftCreateRequest();
DefaultAlipayClient defaultAlipayClient = AlipayClient.getDefaultClient(configDto);
request.setBizModel(entryReqDto);
AntMerchantExpandIndirectZftCreateResponse response = defaultAlipayClient.execute(request);
log.info("支付宝进件结果:{}", response.getBody());
if (response.isSuccess()) {
respDto.setStatus(PayCst.EntryStatus.INIT);
respDto.setEntryId(response.getOrderId());
respDto.setErrorMsg("");
} else {
respDto.setStatus(PayCst.EntryStatus.REJECTED);
respDto.setEntryId("");
respDto.setErrorMsg(response.getSubMsg());
}
} catch (Exception e) {
log.error("支付宝进件报错:{}", e.getMessage(), e);
respDto.setStatus(PayCst.EntryStatus.REJECTED);
respDto.setEntryId("");
respDto.setErrorMsg(e.getMessage());
}
return respDto;
}
/**
* 上传图片
*
* @param configDto 配置信息
* @param url 图片地址
* @return 图片ID
*/
public static String uploadImage(AlipayConfigDto configDto, String url) {
try {
byte[] bytes = UploadFileUtil.downloadImage(url);
Path tempFile = Files.createTempFile("image_", ".png");
// 写入数据
Files.write(tempFile, bytes);
File file = tempFile.toFile();
AntMerchantExpandIndirectImageUploadRequest request = new AntMerchantExpandIndirectImageUploadRequest();
request.setImageType(UploadFileUtil.extractImageExtension(url));
// 设置图片内容
FileItem imageContent = new FileItem(file);
request.setImageContent(imageContent);
DefaultAlipayClient defaultAlipayClient = AlipayClient.getDefaultClient(configDto);
AntMerchantExpandIndirectImageUploadResponse response = defaultAlipayClient.execute(request);
return response.getImageId();
} catch (Exception e) {
log.error("支付宝上传图片报错URL{},错误信息:{}", "url", e.getMessage(), e);
return "";
}
}
/**
* 查询支行
*
* @param instId 顶级机构ID CMB
* @param province 省份 陕西省
* @param city 城市 西安市
*/
public static List<BankBranchDto> queryBankBranchList(AlipayConfigDto configDto, String instId, String province, String city) {
try {
AlipayFinancialnetAuthPbcnameQueryRequest request = new AlipayFinancialnetAuthPbcnameQueryRequest();
AlipayFinancialnetAuthPbcnameQueryModel model = new AlipayFinancialnetAuthPbcnameQueryModel();
model.setInstId(instId);
model.setProvice(province);
model.setCity(city);
request.setBizModel(model);
DefaultAlipayClient defaultAlipayClient = AlipayClient.getDefaultClient(configDto);
AlipayFinancialnetAuthPbcnameQueryResponse response = defaultAlipayClient.execute(request);
String data = response.getPbcQueryResult();
return JSONArray.parseArray(data, BankBranchDto.class);
} catch (Exception e) {
log.error("支付宝查询支行报错,错误信息:{}", e.getMessage(), e);
return new ArrayList<>();
}
}
/**
* 构建进件参数
*
* @param reqDto 请求参数
* @return 进件参数
*/
private static AntMerchantExpandIndirectZftCreateModel buildEntryParams(AggregateMerchantDto reqDto) {
AntMerchantExpandIndirectZftCreateModel entryParams = new AntMerchantExpandIndirectZftCreateModel();
MerchantBaseInfoDto baseInfo = reqDto.getMerchantBaseInfo();
LegalPersonInfoDto legalPersonInfo = reqDto.getLegalPersonInfo();
BusinessLicenceInfoDto businessLicenceInfo = reqDto.getBusinessLicenceInfo();
StoreInfoDto storeInfo = reqDto.getStoreInfo();
SettlementInfoDto settlementInfo = reqDto.getSettlementInfo();
entryParams.setExternalId(reqDto.getMerchantCode());
if ("0".equals(baseInfo.getUserType())) {
entryParams.setMerchantType("07");
} else if ("1".equals(baseInfo.getUserType())) {
switch (baseInfo.getCompanyChildType()) {
case "1" -> entryParams.setMerchantType("01");
case "2" -> entryParams.setMerchantType("02");
case "3" -> entryParams.setMerchantType("05");
case "4" -> entryParams.setMerchantType("04");
default -> throw new CzgException("主体类型错误");
}
} else {
throw new CzgException("用户类型错误");
}
entryParams.setName(businessLicenceInfo.getLicenceName());
entryParams.setAliasName(baseInfo.getShortName());
entryParams.setMcc(baseInfo.getMccCode());
entryParams.setService(List.of("当面付", "jsapi支付", "小程序支付"));
entryParams.setInDoorImages(List.of(storeInfo.getInsidePic().getAlipayId(), storeInfo.getCashierDeskPic().getAlipayId()));
entryParams.setOutDoorImages(List.of(storeInfo.getDoorPic().getAlipayId()));
// 证件类型(目前只支持个体和企业,所以都是营业执照)
entryParams.setCertType("201");
entryParams.setCertImage(businessLicenceInfo.getLicensePic().getAlipayId());
entryParams.setCertNo(businessLicenceInfo.getLicenceNo());
entryParams.setLegalName(legalPersonInfo.getLegalPersonName());
entryParams.setLegalCertNo(legalPersonInfo.getLegalPersonId());
entryParams.setLegalCertFrontImage(legalPersonInfo.getIdCardFrontPic().getAlipayId());
entryParams.setLegalCertBackImage(legalPersonInfo.getIdCardBackPic().getAlipayId());
entryParams.setServicePhone(PayCst.ContactPersonType.LEGAL.equals(baseInfo.getContactPersonType()) ? legalPersonInfo.getLegalPersonPhone() : baseInfo.getContactPhone());
com.alipay.api.domain.AddressInfo addressInfo = new com.alipay.api.domain.AddressInfo();
addressInfo.setCityCode(storeInfo.getMercCityCode());
addressInfo.setDistrictCode(storeInfo.getMercAreaCode());
addressInfo.setProvinceCode(storeInfo.getMercProvCode());
addressInfo.setAddress(storeInfo.getBusinessAddress());
entryParams.setBusinessAddress(addressInfo);
com.alipay.api.domain.SettleCardInfo settleCardInfo = new com.alipay.api.domain.SettleCardInfo();
settleCardInfo.setAccountHolderName(settlementInfo.getSettlementName());
settleCardInfo.setAccountNo(settlementInfo.getSettlementCardNo());
settleCardInfo.setAccountInstProvince(settlementInfo.getOpenAccProvince());
settleCardInfo.setAccountInstCity(settlementInfo.getOpenAccCity());
settlementInfo.setBankBranchName(settlementInfo.getBankBranchName());
settleCardInfo.setUsageType("21".equals(settlementInfo.getSettlementCardType()) ? "01" : "02");
settleCardInfo.setAccountType("DC");
settleCardInfo.setAccountInstName(settlementInfo.getBankName());
settleCardInfo.setAccountInstId(settlementInfo.getBankInstId());
settleCardInfo.setBankCode(settlementInfo.getBankBranchCode());
entryParams.setBizCards(List.of(settleCardInfo));
com.alipay.api.domain.ContactInfo contactInfo = new com.alipay.api.domain.ContactInfo();
if (PayCst.ContactPersonType.SUPER.equals(baseInfo.getContactPersonType())) {
contactInfo.setName(baseInfo.getContactName());
contactInfo.setMobile(baseInfo.getContactPhone());
contactInfo.setEmail(baseInfo.getContactEmail());
contactInfo.setIdCardNo(baseInfo.getContactPersonId());
} else if (PayCst.ContactPersonType.LEGAL.equals(baseInfo.getContactPersonType())) {
contactInfo.setName(legalPersonInfo.getLegalPersonName());
contactInfo.setMobile(legalPersonInfo.getLegalPersonPhone());
contactInfo.setEmail(legalPersonInfo.getLegalPersonEmail());
contactInfo.setIdCardNo(legalPersonInfo.getLegalPersonId());
} else {
throw new CzgException("联系人类型错误");
}
entryParams.setContactInfos(List.of(contactInfo));
entryParams.setLicenseAuthLetterImage(businessLicenceInfo.getLicensePic().getAlipayId());
// 小程序
SiteInfo miniAppInfo = new SiteInfo();
miniAppInfo.setSiteType("06");
miniAppInfo.setSiteUrl("https://sxczgkj.cn");
miniAppInfo.setSiteName("银收客");
miniAppInfo.setTinyAppId("2021004145625815");
entryParams.setSites(List.of(miniAppInfo));
return entryParams;
}
public static void main(String[] args) {
// queryBankBranchList("CMB", "陕西省", "西安市");
uploadImage(null, "https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/20240312/31476c871c224389aea0ac4e17c964a3.jpg");
}
// https://opendocs.alipay.com/solution/0dec7x?pathHash=caec4753 直付通
public static void test() throws ApiException {
ApiClient defaultClient = Configuration.getDefaultApiClient();
// 初始化alipay参数全局设置一次
AlipayConfig alipayConfig = new AlipayConfig();
alipayConfig.setServerUrl("https://openapi.alipay.com");
alipayConfig.setAppId("2021006121646825");
alipayConfig.setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiQkrz+emAuS1mB3KKDOMmAZRd/BlPbh7fAIHAqAj1+QCZNcV3o2BTLIIqnuKpSlFXDG3uDzp2VsBxcizXuBbFyPGylnD9CgCj5abyh3+FIHPAZ2IM3TtpqImZ0TSPGXrMli4Nir7MvZktgccCqQKCC4o6iaDGz+UwWwJUIPna8fm2tiTZ+KH150CZbKVj4ZGNpBh5XSV/1dRgyQIV9D/EwSbkZ0n6VgKQLJBi0C2UE3QB17aL1Ir6+gDXIDbknN8O7GUD3aMGdThYdSRUb5wp9CZ5qfV7vCS/CgaRo38nhH3NOzkTL+7v0m1ZDHPmqEkn9VzZN6sCQdL7PoAOjHOCwIDAQAB");
alipayConfig.setPrivateKey("MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCz3FkdffxZibdwis9W7eOW1dEjGAbSvRDL2ikfeCIW5KZNoIjUqxI0mIoUlLRRKO71QLHZS1Vb2aJp8jeOAqIPa8e76HTneQEzk3FGA8gpraSGvbadHWzvxnmYKsts1TBiEZQL82ySJXhQTJvZ6jyDM7s6wHHUnrH+Qi29QpppQ1sxsoJeCtajUgRg3btD6XbBcyFAX3pzM56Kw9eaIjZoD8WToZOM/Y3sqNL2uo8lLqcIpTrI7Pq5ZOspmBQ+t8v3rS9IdDZZMvd0trzS67AXwHz8rKPBT/lL1A4iHnXCHUvktusX1fPs3/RGY/a7PIddaBLnfY0GcueE16K7QcrXAgMBAAECggEAbSdT2eckp75BWoaTcGEs1tRqeM7TDT/6moyKmnOQ1K3tE31SrSYpBUxxuC3LBNo/sw2RIZtrcTOyMnPyLTgB3DP/4lUf5X51MTTQ8LnI1ypvh+pIki9Sdm3QS33lOOZk149tdpdDk6ozyx/DEcvq74EMpoo2SuAIi5LkKVDrXuehvGA/WeXtpmuPgqRFdIA+JBlA3knHk5XEQY/k2Y31gq5oCwNL9iT9OAZqVkukE6EnvCXE9t2rAV4/snYilaf/UaO+ktgEwSbPBQ8YKlovDAarMBbGtgr6E174A9djlPyR+W/fgx8rlTwSWtieb9MkO8LN3KSxgVs0kY5U8OHg8QKBgQDljJq9kTFHare+W/fAXdUy3tJprfNQCAii6s+GuDfTQiviVQDtWmdtHAN+xU3to7MepvVhwHsqtQnZXKTtZuwwxn82FNl7A5RYD3GVFW+wG6AsGLIdESrWxySoL6Kx8QmNpMEVg8acT/ywzW/RnUXS5vU7GIi8GA0vtyBo24R9KQKBgQDIlf/R9+iNk9oXlbB/k4um9eVvBBS7l5cx4E5Id5Dpp4kGZfPZEa7oDsEUstZZM9mgQLJK/noNWbcf0+BohCR5ux7SC12qIoxwN3k4BzTDqrS8BzFuVVp5PELUsf/uCbRn05iMzpiDUhj3Vde04wvjHYIobfKlZO2HeSWXCpUH/wKBgQC8wSuU6ck90pEY5QMKmZ3wYK1g3PsQOirv3Gmde+nbu7PePsuuYQJfBAQTwCZeXJezgtKP+PjOm2Nn6vhrhpB9YxvD2s0ijET1TG23i5L1myHQYNZFdJJnXgXUjqcX7v5ODMYA7QTqEBPXRnbGRK7fx66rU3dMQ/LD46+wyaFeUQKBgA4QTk53dkuu6SSsLyLSwoDjTsHY5Gc+urAZjQORtoxbXcUgEtfOYJgOqMT9wP+iHgkZYCbX7tDO0IMfxOUvFqueTgvmFhwergAUM6CVCMMLTf689l9JBr3nVrw4+rvC3G5HLLP6rEDQ2cVFtIkPPj8fS4fwJYopKGpOOS9843QbAoGBAMoHH8LqoZ50FLsospx/hJe24Cd8wCgQTXSa/hMqNZ1999JDhftMt7R0ZdB1he2LReACe0K9ntBU4H4u225zZ3wZlyOfoyerAHuLK/ysNlgIIzblZlOxbBJ64Kul8leXzlYy3tOZuZ997KqBcWPCE3LUBBNvM6E3blJUnlmJAVoi");
defaultClient.setAlipayConfig(alipayConfig);
AlipayOpenAgentApi api = new AlipayOpenAgentApi();
AlipayOpenAgentCreateModel data = new AlipayOpenAgentCreateModel();
ContactModel contactInfo = new ContactModel();
contactInfo.setContactEmail("zhangsan@alipy.com");
contactInfo.setContactName("张三");
contactInfo.setContactMobile("18866668888");
data.setContactInfo(contactInfo);
// data.setOrderTicket("00ee2d475f374ad097ee0f1ac223fX00");
data.setAccount("1157756119@qq.com");
try {
AlipayOpenAgentCreateResponseModel response = api.create(data);
System.out.println(response);
} catch (ApiException e) {
AlipayOpenAgentCreateDefaultResponse errorObject = (AlipayOpenAgentCreateDefaultResponse) e.getErrorObject();
System.out.println("调用失败:" + errorObject);
System.out.println("调用失败:" + e.getMessage());
}
}
}

View File

@@ -0,0 +1,224 @@
package com.czg.third.alipay;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.*;
import com.alipay.api.request.AntMerchantExpandIndirectZftSimplecreateRequest;
import com.alipay.api.response.AntMerchantExpandIndirectZftSimplecreateResponse;
import java.util.ArrayList;
import java.util.List;
/**
* @author yjjie
* @date 2026/1/5 13:46
*/
public class AlipayReqUtils {
public static void main(String[] args) throws AlipayApiException {
// DefaultAlipayClient defaultAlipayClient = AlipayClient.getDefaultClient(null);
// AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig());
DefaultAlipayClient defaultAlipayClient = new DefaultAlipayClient(getAlipayConfig());
// 构造请求参数以调用接口
AntMerchantExpandIndirectZftSimplecreateRequest request = new AntMerchantExpandIndirectZftSimplecreateRequest();
AntMerchantExpandIndirectZftSimplecreateModel model = new AntMerchantExpandIndirectZftSimplecreateModel();
// 设置商户编号
model.setExternalId("1052900599901941");
// 设置签约支付宝账户
model.setBindingAlipayLogonId("18434286340");
// 设置商户别名
model.setAliasName("一点点");
// 设置商户客服电话
// model.setServicePhone("0571-85022088");
// 设置商户联系人信息
ContactInfo contactInfos = new ContactInfo();
contactInfos.setIdCardNo("142725199902176419");
// contactInfos.setPhone("0571-85022088");
contactInfos.setName("王伟");
contactInfos.setMobile("18434286340");
contactInfos.setEmail("18434286340");
model.setContactInfos(contactInfos);
// 设置进件的二级商户名称
model.setName("王伟");
// 设置默认结算规则
DefaultSettleRule defaultSettleRule = new DefaultSettleRule();
defaultSettleRule.setDefaultSettleType("alipayAccount");
defaultSettleRule.setDefaultSettleTarget("18434286340");
model.setDefaultSettleRule(defaultSettleRule);
// 设置结算支付宝账号
model.setAlipayLogonId("18434286340");
// 设置结算银行卡信息
SettleCardInfo bizCards = new SettleCardInfo();
bizCards.setAccountInstName("招商银行");
bizCards.setBankCode("103290003044");
bizCards.setAccountType("DC");
bizCards.setUsageType("01");
bizCards.setAccountHolderName("王伟");
bizCards.setAccountInstCity("杭州市");
bizCards.setAccountInstId("CMB");
bizCards.setAccountNo("6214831259609102");
bizCards.setAccountBranchName("招商银行杭州高新支行");
bizCards.setAccountInstProvince("浙江省");
model.setBizCards(bizCards);
// 设置授权函
model.setLicenseAuthLetterImage("c6c0c7a1-b9d5-4e5d-b9d4-9eed39f00e65.jpg");
// 设置开票资料信息
// MerchantInvoiceInfo invoiceInfo = new MerchantInvoiceInfo();
// invoiceInfo.setMailTelephone("057162288888");
// invoiceInfo.setTaxPayerQualification("01");
// invoiceInfo.setAddress("浙江省杭州市西湖区西溪路蚂蚁金服");
// invoiceInfo.setAcceptElectronic(false);
// invoiceInfo.setTelephone("057162288888");
// invoiceInfo.setTitle("蚂蚁金服(杭州)信息技术有限公司");
// invoiceInfo.setMailName("张三");
// invoiceInfo.setAutoInvoice(true);
// invoiceInfo.setTaxPayerValid("19981011");
// invoiceInfo.setTaxNo("51010482542598631219");
// invoiceInfo.setBankName("中国银行");
// AddressInfo mailAddress = new AddressInfo();
// mailAddress.setAddress("万塘路18号黄龙时代广场B座");
// mailAddress.setDistrictCode("371002");
// mailAddress.setLatitude("60.270001");
// mailAddress.setCityCode("371000");
// mailAddress.setPoiid("B0FFIVU189");
// mailAddress.setProvinceCode("370000");
// mailAddress.setLongitude("120.760001");
// invoiceInfo.setMailAddress(mailAddress);
// invoiceInfo.setBankAccount("1234567812345678123");
// model.setInvoiceInfo(invoiceInfo);
// 设置商户使用服务
List<String> service = new ArrayList<String>();
service.add("当面付");
model.setService(service);
// 设置经营地址
AddressInfo businessAddress = new AddressInfo();
businessAddress.setAddress("万塘路18号黄龙时代广场B座");
businessAddress.setDistrictCode("371002");
businessAddress.setLatitude("60.270001");
businessAddress.setCityCode("371000");
businessAddress.setPoiid("B0FFIVU189");
businessAddress.setProvinceCode("370000");
businessAddress.setType("BUSINESS_ADDRESS");
businessAddress.setLongitude("120.760001");
model.setBusinessAddress(businessAddress);
// 设置门头照
model.setOutDoorImages("c6c0c7a1-b9d5-4e5d-b9d4-9eed39f00e65.jpg");
// 设置内景照
model.setInDoorImages("c6c0c7a1-b9d5-4e5d-b9d4-9eed39f00e65.jpg");
// 设置商户站点信息
// SiteInfo sites = new SiteInfo();
// sites.setIcpOrgName("支付宝(中国)网络技术有限公司");
// sites.setSiteType("01");
// sites.setSiteDomain("www.alipay.com");
// sites.setScreenshotImage("c6c0c7a1-b9d5-4e5d-b9d4-9eed39f00e65.jpg");
// sites.setRemark("备注说明");
// sites.setAuthLetterImage("c6c0c7a1-b9d5-4e5d-b9d4-9eed39f00e65.jpg");
// sites.setSiteName("XXX网站");
// sites.setMarket("豌豆荚");
// sites.setPassword("测试密码");
// sites.setDownload("https://itunes.apple.com/cn/app/id333206289?mt=8");
// sites.setTinyAppId("2021004105652035");
// sites.setSiteUrl("https://open.alipay.com");
// sites.setIcpServiceName("支付宝");
// sites.setRemarkImage("c6c0c7a1-b9d5-4e5d-b9d4-9eed39f00e65.jpg");
// sites.setAccount("测试账号");
// sites.setIcpNo("沪ICP备15027489号-2");
// sites.setStatus("ONLINE");
// model.setSites(sites);
// 设置商户类别码 mcc
model.setMcc("B0007");
// 设置商户行业资质图片
List<IndustryQualificationInfo> qualifications = new ArrayList<IndustryQualificationInfo>();
IndustryQualificationInfo qualifications0 = new IndustryQualificationInfo();
qualifications0.setIndustryQualificationImage("c6c0c7a1-b9d5-4e5d-b9d4-9eed39f00e65.jpg");
qualifications0.setIndustryQualificationType("323");
qualifications.add(qualifications0);
model.setQualifications(qualifications);
// 设置补充证件号
model.setAdditionalCertNo("9133010608210550XR");
// 设置补充证件类型
model.setAdditionalCertType("201");
// 设置补充证件图片
model.setAdditionalCertImage("c6c0c7a1-b9d5-4e5d-b9d4-9eed39f00e65.jpg");
// uid参数未来计划废弃存量商户可继续使用新商户请使用openid。请根据应用-开发配置-openid配置选择支持的字段。
// model.setInfoSourceUid("2088111122223333");
// 设置平替原来的info_source_uid字段
// model.setInfoSourceOpenId("074a1CcTG1LelxKe4xQC0zgNdId0nxi95b5lsNpazWYoCo5");
// uid参数未来计划废弃存量商户可继续使用新商户请使用openid。请根据应用-开发配置-openid配置选择支持的字段。
// model.setOverseaSettleAccount("2088111122223333");
// 设置平替原来的oversea_settle_open_id字段
// model.setOverseaSettleOpenId("074a1CcTG1LelxKe4xQC0zgNdId0nxi95b5lsNpazWYoCo5");
// 设置二级商户与服务商的签约时间
model.setSignTimeWithIsv("2015-04-15");
// 设置代扣产品信息
ZFTWithholdingInfo zftWithholdingInfo = new ZFTWithholdingInfo();
zftWithholdingInfo.setWithholdingServiceFeatureName("GENERAL_WITHHOLDING_P");
zftWithholdingInfo.setSignScene("DEFAULT");
model.setZftWithholdingInfo(zftWithholdingInfo);
// 设置交易场景
List<String> tradeScene = new ArrayList<String>();
tradeScene.add("TINY_APP");
model.setTradeScene(tradeScene);
request.setBizModel(model);
// 第三方代调用模式下请设置app_auth_token
// request.putOtherTextParam("app_auth_token", "<-- 请填写应用授权令牌 -->");
AntMerchantExpandIndirectZftSimplecreateResponse response = defaultAlipayClient.execute(request);
System.out.println(response.getBody());
if (response.isSuccess()) {
System.out.println("调用成功");
} else {
System.out.println("调用失败");
// sdk版本是"4.38.0.ALL"及以上,可以参考下面的示例获取诊断链接
// String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response);
// System.out.println(diagnosisUrl);
}
}
private static AlipayConfig getAlipayConfig() {
String privateKey = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD48HaUoV7OH7os+9L01kHgxzwIhJick4OkFq4aHsntsXEJ J3gedhuEZtV8oHKZ30DPW12IJ4S8NXtpr8OWaqrAPFonf4wVaRY1d0yIAea57kfLEn9oOEEy4FzARgMNDkyxC+/3OUdGbLHpTjfVX3gusXsEhUDy1/WewihAkoNYF37+W3W/uVLzeWoPq0EcUbRv/G/t/p6gL69ltsMAiVFG4Q/Yk24YAN6lYgBPNLXUEwQ1Q+T+1omjfavHgvarKOp33z3JOUH+aGOmDsJ5Y9gyGtJzOCipAd8Zcv+T1ygsEzZYO1/gzcbPnfO1ShqStCHzssuw8FBVx2JdfQKXKMMNAgMBAAECggEAVTrO/pg5Q00titU1Jspsh67u6OOs9H605Ws2dI7yB8VmtAGlaJh7V1t14FN2qSP8poHbhhAxq9aLyGV7C3a9u09udnN+3J28EtYjh7VO732bavWMVXxdJjQWzWWrCb9JlpxFrlkYBA6W4w/6ob0sAqCVQ7jzwbEa0R4cde8ztOa5nysKSfr4YTSs0gqvoiC6fmg8eiRJraEQBoYz9VkKFtOhhh/4w5FhVcYQ2gQvZ3kK3QVuD1eJIQKlCtz8qaox9lXKDiZT4SCmnKshdUL0u5TYIcYeBjZmhJz0Q50KHcpZrCs5y7I0+vRBH3hU+TKSQt7ureymwhbwWMHScLV2gQKBgQD+58SHXhr5M8NGagAmTdsgmCnNv2kOYMd4STyPMY10SVwCv1Bk808ZuP+7e558J1b5/OuDLI5dLq6xrZ/1wLv1G++XqxI00hlFuWS5mUGJVcXotT1mw20rVeUILc7Qe3mLvbMGgfyKf4A7Qa5SSZ4bDeDTJYaFxyiQ281hMzDuPQKBgQD6AiL/Na2/uPH4CG6juwpjYvYVUcjK+7gbRwf3wWsWMpk90Z4ju2iUiP5c1J/oK9P+1T3PIr6M4Xjza8JJj+r9KC/PVB0gBv6vVM96cDpKUEy/UMpcn/T81vqj/Z+WEOODU8Ms6NiTTm+u9ldvpCjbu0u8M+9c0JeIyadJvSTFEQKBgQCsxmFyM3nq8YfpgU2qqNjfBeRH3faSVUy+nj1a/YZYjKS+A/i1BCnYUImeBVNN6chNV342ggvY4xxruDiU9Vcw8wd58O09Oi8BEIFSP6upL6cebUI6Fjo3xlegLJRiwV6INkNTJOYM5hD/mSxUACwXQFfkJipBINXBIgraWD1RLQKBgQCj49axWq0F6+WjZVOyPaD3uh37p9trRUxRhWTxw3fB23WdktaKMgbCqHOmwzP4bRLSEVQtf2dOz1gMqu14b8HqJvgAf/F/11YJ9hz09LEhmjZVjE68HZfqT7uK2W5OX8/lfXmK7TFcj6SjG5YB96lZMhTZ0WnufEd6QkdKDZYXIQKBgQD9GDTcIMbFwbEaKHnfZaTD3f876EGRgsgrCxwdEk7LBCRPwWo7yI929M4psIlpNwNeiyjBkBunWIVkpznp6qPtJqagIPUYesU4f5v6/okq5wcpaNKSkWbIvWVLaLGOiA1aeGJtbpMpyClbSr52puHpRRdvAiIEQ74yYh0JX8q96g==";
String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiQkrz+emAuS1mB3KKDOMmAZRd/BlPbh7fAIHAqAj1+QCZNcV3o2BTLIIqnuKpSlFXDG3uDzp2VsBxcizXuBbFyPGylnD9CgCj5abyh3+FIHPAZ2IM3TtpqImZ0TSPGXrMli4Nir7MvZktgccCqQKCC4o6iaDGz+UwWwJUIPna8fm2tiTZ+KH150CZbKVj4ZGNpBh5XSV/1dRgyQIV9D/EwSbkZ0n6VgKQLJBi0C2UE3QB17aL1Ir6+gDXIDbknN8O7GUD3aMGdThYdSRUb5wp9CZ5qfV7vCS/CgaRo38nhH3NOzkTL+7v0m1ZDHPmqEkn9VzZN6sCQdL7PoAOjHOCwIDAQAB";
AlipayConfig alipayConfig = new AlipayConfig();
alipayConfig.setServerUrl("https://openapi.alipay.com/gateway.do");
alipayConfig.setAppId("2021004174605036");
alipayConfig.setPrivateKey(privateKey);
alipayConfig.setFormat("json");
alipayConfig.setAlipayPublicKey(alipayPublicKey);
alipayConfig.setCharset("UTF-8");
alipayConfig.setSignType("RSA2");
return alipayConfig;
}
}

View File

@@ -0,0 +1,47 @@
package com.czg.third.alipay.dto.config;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @author yjjie
* @date 2026/1/4 13:49
*/
@Data
@Accessors(chain = true)
public class AlipayConfigDto {
/**
* 支付宝 AppId
*/
private String appId;
/**
* 商户私钥
*/
private String privateKey;
/**
* 支付宝公钥
*/
private String alipayPublicKey;
/**
* 支付宝支付域名
* <a href="https://openapi.alipay.com/gateway.do"></a>
*/
private String domain;
public static AlipayConfigDto getDefaultConfig() {
return new AlipayConfigDto()
.setAppId("2021004174605036")
.setPrivateKey("MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD48HaUoV7OH7os+9L01kHgxzwIhJick4OkFq4aHsntsXEJ J3gedhuEZtV8oHKZ30DPW12IJ4S8NXtpr8OWaqrAPFonf4wVaRY1d0yIAea57kfLEn9oOEEy4FzARgMNDkyxC+/3OUdGbLHpTjfVX3gusXsEhUDy1/WewihAkoNYF37+W3W/uVLzeWoPq0EcUbRv/G/t/p6gL69ltsMAiVFG4Q/Yk24YAN6lYgBPNLXUEwQ1Q+T+1omjfavHgvarKOp33z3JOUH+aGOmDsJ5Y9gyGtJzOCipAd8Zcv+T1ygsEzZYO1/gzcbPnfO1ShqStCHzssuw8FBVx2JdfQKXKMMNAgMBAAECggEAVTrO/pg5Q00titU1Jspsh67u6OOs9H605Ws2dI7yB8VmtAGlaJh7V1t14FN2qSP8poHbhhAxq9aLyGV7C3a9u09udnN+3J28EtYjh7VO732bavWMVXxdJjQWzWWrCb9JlpxFrlkYBA6W4w/6ob0sAqCVQ7jzwbEa0R4cde8ztOa5nysKSfr4YTSs0gqvoiC6fmg8eiRJraEQBoYz9VkKFtOhhh/4w5FhVcYQ2gQvZ3kK3QVuD1eJIQKlCtz8qaox9lXKDiZT4SCmnKshdUL0u5TYIcYeBjZmhJz0Q50KHcpZrCs5y7I0+vRBH3hU+TKSQt7ureymwhbwWMHScLV2gQKBgQD+58SHXhr5M8NGagAmTdsgmCnNv2kOYMd4STyPMY10SVwCv1Bk808ZuP+7e558J1b5/OuDLI5dLq6xrZ/1wLv1G++XqxI00hlFuWS5mUGJVcXotT1mw20rVeUILc7Qe3mLvbMGgfyKf4A7Qa5SSZ4bDeDTJYaFxyiQ281hMzDuPQKBgQD6AiL/Na2/uPH4CG6juwpjYvYVUcjK+7gbRwf3wWsWMpk90Z4ju2iUiP5c1J/oK9P+1T3PIr6M4Xjza8JJj+r9KC/PVB0gBv6vVM96cDpKUEy/UMpcn/T81vqj/Z+WEOODU8Ms6NiTTm+u9ldvpCjbu0u8M+9c0JeIyadJvSTFEQKBgQCsxmFyM3nq8YfpgU2qqNjfBeRH3faSVUy+nj1a/YZYjKS+A/i1BCnYUImeBVNN6chNV342ggvY4xxruDiU9Vcw8wd58O09Oi8BEIFSP6upL6cebUI6Fjo3xlegLJRiwV6INkNTJOYM5hD/mSxUACwXQFfkJipBINXBIgraWD1RLQKBgQCj49axWq0F6+WjZVOyPaD3uh37p9trRUxRhWTxw3fB23WdktaKMgbCqHOmwzP4bRLSEVQtf2dOz1gMqu14b8HqJvgAf/F/11YJ9hz09LEhmjZVjE68HZfqT7uK2W5OX8/lfXmK7TFcj6SjG5YB96lZMhTZ0WnufEd6QkdKDZYXIQKBgQD9GDTcIMbFwbEaKHnfZaTD3f876EGRgsgrCxwdEk7LBCRPwWo7yI929M4psIlpNwNeiyjBkBunWIVkpznp6qPtJqagIPUYesU4f5v6/okq5wcpaNKSkWbIvWVLaLGOiA1aeGJtbpMpyClbSr52puHpRRdvAiIEQ74yYh0JX8q96g==")
.setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiQkrz+emAuS1mB3KKDOMmAZRd/BlPbh7fAIHAqAj1+QCZNcV3o2BTLIIqnuKpSlFXDG3uDzp2VsBxcizXuBbFyPGylnD9CgCj5abyh3+FIHPAZ2IM3TtpqImZ0TSPGXrMli4Nir7MvZktgccCqQKCC4o6iaDGz+UwWwJUIPna8fm2tiTZ+KH150CZbKVj4ZGNpBh5XSV/1dRgyQIV9D/EwSbkZ0n6VgKQLJBi0C2UE3QB17aL1Ir6+gDXIDbknN8O7GUD3aMGdThYdSRUb5wp9CZ5qfV7vCS/CgaRo38nhH3NOzkTL+7v0m1ZDHPmqEkn9VzZN6sCQdL7PoAOjHOCwIDAQAB")
.setDomain("https://openapi.alipay.com/gateway.do");
// return new AlipayConfigDto()
// .setAppId("2021004145625815")
// .setPrivateKey("MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCAjDBuS8K/IJb9ui+KuNm/sTUdEiaji4BNpZ92avO1N5JpNlGmac6ec4p3tNFT950sBLcQkClcUpQxUHQzAT6DYNNXOKyvfI/EmcqwCw6PaMNLs/8cV//J2WWZBUhLaOsjKurpm9/3W5MnTh4BGxIfBoeBMA8f8K3BgKdmyKtvIEV2h2cyjsMskdn+g6oNZcmWcms0pvpPHyH46mRaGFhpp0v19wX3WsamGldh1L2VntmaDN3C2XbSrXv90XYp5bEUqwTbLwXpMAlzTibF56d/iqv9oYi8cpAKougUFLOymnbutLNs2tLrEDSFwHcmG2/wbZHybZyYcIgFgv4arf+tAgMBAAECggEAf7hKKlw1y6Z6vvAtalxNZUuRZSfyog3p1bwYWxTavZPQcZ7Zs0lvVDmiO1u5m/7q96BbryY9IhCeUv0H5uF2lhwu/3s9AEL3qTPQkeb6eXxyhhX6A9RfPdM1Qbtg4CQHdHKg4qjP9znSVHwmDZ0y/QaEvdPdQzPjv92u9c2tn4N4x6XyBYcU5gzxiJNnIugCmBgcJo/3H2fgV+XXEhORPvy5of9b4oATHEaLS/8dAS2wuOjhzaGS4MXp3VkXn3XaYjwSzaL03qYWA+xm+aO5sJv8bmqZW7sNVck5o3sPo7cQ4VkBFVzyrRdmJcxcSRJ9MsB9JsrhoKI8pgaXrVie4QKBgQDU2vai0lpBIK/0jzRpPNoqdT8lnafnnWni8nU4kfAh+gCLi+HBPhQRT0kv4unQc2q2/gALE7sgZVO00JGY5a3R0orsojPoUSZlpypGW7GGqKy576NHn0nw4o/PdfysT92VWgt1hlfTf6qfCDhfE9APU+RGvlSWXcT8nxVel3iUaQKBgQCamoJN6+4v+chJvL2nqV8NVVRLp0vDIHxs1QOtKwUodx8Qp1D6CJYtavCXn8aNUFVNQJPJ7TQPpJjXP2rI4SN01weDwx+I+wh8PBGHV6/234R+6TvFgY1PrYgCdfNP4i/E7B4uyEhAxdU73PB8qkqRAeJGok05p7oG71KCOBiYpQKBgEZfGflcuDAeAW5GRhqg3rP4zWa/R7qgZVh9tll8jjp9b96y4XFE99d9MgId8BVVgyt6sEL5Q/2C4ni+F9TH4n6jMADp42VkJuCmsqhOOlP9whU67+2G8Sgtj0QUivPg964f9ffl8XVgGOW5DwIIB9p5btggptCLscufQK5kP545AoGADBvf6tR4wl8w9b2HqTMV08iEIqzGvVC1Dh0c/Zop/EJgN4CzUfIMOSBwGaAVAApzs+pD6QPgGP2OTwWTioo/qa4R05sbxDHNN1XJFa2jhZV6HiqMWOrNs5jm1zJ/zRjtHuJTdtyO9CvKiLbESy9XScY4/8lEfSiK5HIoJzTXkFUCgYAkYkvkW6psJpWj05XWq44UN0n6QOU/Igl35Um/iuOMVhsTmIt09STQVTuzJzfH82+sCqoRsD1blE5unKNUC1DK77aNKTv3Z0dxN9R7FAyfZRiYQXTrbBPBqWjay6FCNxn8e8UsJN4Z6FIV2LGlQI114krSap1MALKLVvnld0NaUQ==")
// .setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiQkrz+emAuS1mB3KKDOMmAZRd/BlPbh7fAIHAqAj1+QCZNcV3o2BTLIIqnuKpSlFXDG3uDzp2VsBxcizXuBbFyPGylnD9CgCj5abyh3+FIHPAZ2IM3TtpqImZ0TSPGXrMli4Nir7MvZktgccCqQKCC4o6iaDGz+UwWwJUIPna8fm2tiTZ+KH150CZbKVj4ZGNpBh5XSV/1dRgyQIV9D/EwSbkZ0n6VgKQLJBi0C2UE3QB17aL1Ir6+gDXIDbknN8O7GUD3aMGdThYdSRUb5wp9CZ5qfV7vCS/CgaRo38nhH3NOzkTL+7v0m1ZDHPmqEkn9VzZN6sCQdL7PoAOjHOCwIDAQAB")
// .setDomain("https://openapi.alipay.com/gateway.do");
}
}

View File

@@ -0,0 +1,43 @@
package com.czg.third.alipay.dto.entry;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* 经营地址
* @author yjjie
* @date 2026/1/4 11:20
*/
@Data
public class AlipayAddressReqDto {
/**
* 【必填】
* 城市编码
* 蚂蚁店铺请按照蚂蚁店铺地区码 表格中填写。
* 直付通商户请按照直付通商户地区码 表格中内容填写。
*/
@JSONField(name = "city_code")
private String cityCode;
/**
* 【必填】
* 区县编码
*/
@JSONField(name = "district_code")
private String districtCode;
/**
* 【必填】
* 详细地址
*/
@JSONField(name = "address")
private String address;
/**
* 【必填】
* 省份编码
*/
@JSONField(name = "province_code")
private String provinceCode;
}

View File

@@ -0,0 +1,89 @@
package com.czg.third.alipay.dto.entry;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* 结算银行卡信息
* @author yjjie
* @date 2026/1/4 11:14
*/
@Data
public class AlipayBizCardsReqDto {
/**
* 【必填】
* 开户支行名
*/
@JSONField(name = "account_branch_name")
private String accountBranchName;
/**
* 【必填】
* 卡户名
*/
@JSONField(name = "account_holder_name")
private String accountHolderName;
/**
* 【必填】
* 开户行所在地-省
*/
@JSONField(name = "account_inst_province")
private String accountInstProvince;
/**
* 【必填】
* 开户行所在地-市
*/
@JSONField(name = "account_inst_city")
private String accountInstCity;
/**
* 【必填】
* 开户行简称缩写
*/
@JSONField(name = "account_inst_id")
private String accountInstId;
/**
* 【必填】
* 银行名称
*/
@JSONField(name = "account_inst_name")
private String accountInstName;
/**
* 【必填】
* 银行卡号
*/
@JSONField(name = "account_no")
private String accountNo;
/**
* 【必填】
* 银行卡类型
* 【枚举值】
* 借记卡: DC
* 信用卡: CC
*/
@JSONField(name = "account_type")
private String accountType;
/**
* 【必填】
* 账号使用类型
* 【枚举值】
* 对公: 01
* 对私: 02
*/
@JSONField(name = "usage_type")
private String usageType;
/**
* 【选填】
* 联行号
*/
@JSONField(name = "bank_code")
private String bankCode;
}

View File

@@ -0,0 +1,50 @@
package com.czg.third.alipay.dto.entry;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* 商户联系人信息
* @author yjjie
* @date 2026/1/4 11:02
*/
@Data
public class AlipayContactInfoReqDto {
/**
* 【必填】
* 联系人名字
*/
@JSONField(name = "name")
private String name;
/**
* 【必填】
* email | mobile | phone 三选一
* 电子邮箱
*/
@JSONField(name = "email")
private String email;
/**
* 【必填】
* email | mobile | phone 三选一
* 手机号码
*/
@JSONField(name = "mobile")
private String mobile;
/**
* 【必填】
* email | mobile | phone 三选一
* 电话
*/
@JSONField(name = "phone")
private String phone;
/**
* 【必填】
* 身份证号
*/
@JSONField(name = "id_card_no")
private String idCardNo;
}

View File

@@ -0,0 +1,249 @@
package com.czg.third.alipay.dto.entry;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.util.List;
/**
* 平台商提交二级商户资料进行进件,完成二级商户入驻
* <a href="https://opendocs.alipay.com/solution/9434dd99_ant.merchant.expand.indirect.zft.simplecreate?scene=bf5951260023430e944c2e9afdf7f9e2&pathHash=d3136936">...</a>
* @author yjjie
* @date 2025/12/29 14:19
*/
@Data
public class AlipayEntryReqDto {
/**
* 【必填】
* 商户编号,由一级商户定义,保证在一级商户下唯一即可
*/
@JSONField(name = "external_id")
private String externalId;
/**
* 【必填】
* 商户别名。支付宝收银台及账单中的商户名称会展示此处设置的别名。如果涉及支付宝APP内的支付支付结果页也会展示该别名如果涉及线下当面付场景请填写线下店铺名称
*/
@JSONField(name = "alias_name")
private String aliasName;
/**
* 【必填】
* 商户联系人信息。在本业务中ContactInfo对象中联系人姓名、手机号必填其他选填
*/
@JSONField(name = "contact_infos")
private AlipayContactInfoReqDto contactInfo;
/**
* 【必填】
* 默认结算规则。当调用收单接口settle_info中设置默认结算规则defaultSettle交易资金将结算至此处设置的默认结算目标账户中。其详细描述及收单接口传参示例参考功能包文档
*/
@JSONField(name = "default_settle_rule")
private AlipaySettleRuleReqDto defaultSettleRule;
/**
* 【必填】
* 商户使用服务
* 可选值有当面付、jsapi支付、app支付、wap支付、电脑支付、预授权支付、商户代扣、小程序支付、订单码支付。其值会影响其他字段必填性详见其他字段描述
* 当面付: 当面付
* jsapi支付: jsapi支付
* app支付: app支付
* wap支付: wap支付
* 电脑支付: 电脑支付
* 预授权支付: 预授权支付
* 商户代扣: 商户代扣
* 小程序支付: 小程序支付
* 订单码支付: 订单码支付
*/
@JSONField(name = "service")
private List<String> service;
/**
* 【必填】
* 商户证件编号
* 按商户类型merchant_type的说明提供对应的证件编号
*/
@JSONField(name = "cert_no")
private String certNo;
/**
* 【必填】
* 商户类别码 mcc
* <a href="https://mdn.alipayobjects.com/huamei_fctrxv/afts/file/A*3TMHRZ8ppa4AAAAAAAAAAAAADs2DAQ/%E8%BF%9B%E4%BB%B6MCC%E4%B8%8E%E8%B5%84%E8%B4%A8%E8%A6%81%E6%B1%82202212.xlsx">...</a>
* 可查看 进件MCC与资质要求 202212.xlsx特殊行业要按照MCC说明中的资质一栏上传辅助资质辅助资质要在 qualifications 中上传,会有人工审核。
* 【示例值】B0007
*/
@JSONField(name = "mcc")
private String mcc;
/**
* 【选填】
* 结算支付宝账号
* 结算账号使用支付宝账号时必填本字段指定交易资金结算的具体支付宝账号与binding_alipay_logon_id同主体的支付宝账号即可
*/
@JSONField(name = "alipay_logon_id")
private String alipayLogonId;
/**
* 【选填】
* 法人名称
* 非个人商户类型必填
*/
@JSONField(name = "legal_name")
private String legalName;
/**
* 【选填】
* 法人证件编号
* 法人证件编号,非个人商户类型必填
*/
@JSONField(name = "legal_cert_no")
private String legalCertNo;
/**
* 【选填】
* 商户证件图
* 目前只有当商户类型是个人商户且使用当面付服务时才需填写
*/
@JSONField(name = "cert_image_back")
private String certImageBack;
/**
* 【选填】
* 商户证件图片
* 本业务接口中如果是特殊行业必填使用当面付服务时非个人必填个人结算到卡时必填。其值为使用ant.merchant.expand.indirect.image.upload上传图片得到的一串oss key。
*/
@JSONField(name = "cert_image")
private String certImage;
/**
* 【选填】
* 进件的二级商户名称
* 一般情况下要与证件的名称相同。个体工商户类型可以放宽到法人名称
*/
@JSONField(name = "name")
private String name;
/**
* 【选填】
* 法人证件类型
* 默认可不填认为legal_cert_no是大陆身份证。类型包括100 大陆身份证105 港澳居民往来内地通行证106 台湾同胞往来大陆通行证108 外国人居留证
* 【枚举值】
* 大陆身份证: 100
* 港澳居民往来内地通行证: 105
* 台湾同胞往来大陆通行证: 106
* 外国人居留证: 108
*/
@JSONField(name = "legal_cert_type")
private String legalCertType;
/**
* 【选填】
* 商户类型
* 01企业cert_type填写201营业执照cert_no填写营业执照号
* 02事业单位cert_type填写218事业单位法人证书cert_no填写事业单位法人证书编号
* 03民办非企业组织cert_type填写204民办非企业登记证书cert_no填写民办非企业登记证书编号
* 04社会团体cert_type填写206社会团体法人登记证书cert_no填写社会团体法人登记证书编号
* 05党政及国家机关cert_type填写219党政机关批准设立文件/行政执法主体资格证cert_no填写党政机关批准设立文件/行政执法主体资格证编号;
* 06个人商户cert_type填写100个人身份证cert_no填写个人身份证号码
* 07个体工商户cert_type填写201营业执照cert_no填写营业执照编号
*/
@JSONField(name = "merchant_type")
private String merchantType;
/**
* 【选填】
* 商户证件类型
* 按商户类型merchant_type的说明提供对应的证件类型。
* 营业执照: 201
* 事业单位法人证书: 218
* 民办非企业登记证书: 204
* 社会团体法人登记证书: 206
* 党政机关批准设立文件/行政执法主体资格证: 219
* 个人身份证: 100
*/
@JSONField(name = "cert_type")
private String certType;
/**
* 【选填】
* 证件名称
* 目前只有个体工商户商户类型要求填入本字段,填写值为个体工商户营业执照上的名称
*/
@JSONField(name = "cert_name")
private String certName;
/**
* 【选填】
* 结算银行卡信息
* 结算银行卡信息,结算账号使用银行卡时必填。本业务当前只允许传入一张结算卡。个人类型商户不允许结算到银行卡
* 【必选条件】结算银行卡信息,结算账号使用银行卡时必填。本业务当前只允许传入一张结算卡。个人类型商户不允许结算到银行卡
*/
@JSONField(name = "biz_cards")
private AlipayBizCardsReqDto bizCards;
/**
* 【选填】
* 经营地址
* 使用当面付服务时必填。地址对象中省、市、区、地址必填,其余选填
*/
@JSONField(name = "business_address")
private AlipayAddressReqDto businessAddress;
/**
* 【选填】
* 门头照
* 使用当面付服务时必填。其值为使用ant.merchant.expand.indirect.image.upload上传图片得到的一串oss key
*/
@JSONField(name = "out_door_images")
private String outDoorImages;
/**
* 【选填】
* 内景照
* 使用当面付服务时必填。其值为使用ant.merchant.expand.indirect.image.upload上传图片得到的一串oss key
*/
@JSONField(name = "in_door_images")
private String inDoorImages;
/**
* 【选填】
* 授权函
* <a href="https://opendocs.alipay.com/open/direct-payment/cg5mkp#%E7%9B%B8%E5%85%B3%E8%B5%84%E6%96%99">《说明函》模板参考</a>
* 当商户名与结算卡户名不一致。《说明函》模板参考。涉及外籍法人(这种情况上传任意能证明身份的图片)时必填,
* 其值为使用ant.merchant.expand.indirect.image.upload上传图片得到的一串oss key。商户类型为个体工商户时本字段仅需上传营业执照非授权函
*/
@JSONField(name = "license_auth_letter_image")
private String licenseAuthLetterImage;
/**
* 【选填】
* 商户站点信息
* 包括网站、app、小程序。商户使用服务包含电脑支付、wap支付时必须填充一个类型为01(网站)的SiteInfo对象site_type/site_url/site_name必填
* 当包含app支付时必须至少填充类型为02(APP)或06(支付宝小程序)中一种类型的SiteInfo对象site_type/site_name必填当包含jsapi支付时必须填充一个类型为06(支付宝小程序)的SiteInfo对象
*/
@JSONField(name = "sites")
private AlipaySitesReqDto sites;
/**
* 【选填】
* 商户行业资质图片
* 当商户的经营类目选择了特殊行业时该字段必填需要特殊行业资质文件。每项行业资质信息中industry_qualification_type和industry_qualification_image均必填。
*/
@JSONField(name = "qualifications")
private List<AlipayQualificationsReqDto> qualifications;
/**
* 【选填】
* 交易场景
* 【枚举值】
* 小程序支付场景: TINY_APP
* H5场景: WAP
* 线下当面付场景: OFFLINE
* APP支付场景: APP
* 网站支付场景: PC
*/
@JSONField(name = "trade_scene")
private List<String> tradeScene;
}

View File

@@ -0,0 +1,31 @@
package com.czg.third.alipay.dto.entry;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 图片上传
* @author yjjie
* @date 2026/1/4 13:46
*/
@Data
@Accessors(chain = true)
public class AlipayImageUploadReqDto {
/**
* 【必填】
* 图片格式
* 支持格式bmp、jpg、jpeg、png、gif.
*/
@JSONField(name = "image_type")
private String imageType;
/**
* 【必填】
* 图片二进制字节流
* 最大为10M
*/
@JSONField(name = "image_content")
private byte[] imageContent;
}

View File

@@ -0,0 +1,30 @@
package com.czg.third.alipay.dto.entry;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* 商户行业资质
* @author yjjie
* @date 2026/1/4 11:40
*/
@Data
public class AlipayQualificationsReqDto {
/**
* 【必填】
* 商户行业资质类型 具体选值参见 <a href="https://gw.alipayobjects.com/os/bmw-prod/7aa3a36b-2bc2-4d57-815f-08edd55ef67e.xlsx">文档</a>
* 【枚举值】
* 金融许可证: 323
* 【示例值】323
*/
@JSONField(name = "industry_qualification_type")
private String industryQualificationType;
/**
* 【必填】
* 商户行业资质图片
*/
@JSONField(name = "industry_qualification_image")
private String industryQualificationImage;
}

View File

@@ -0,0 +1,29 @@
package com.czg.third.alipay.dto.entry;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* 结算规则
* @author yjjie
* @date 2026/1/4 11:06
*/
@Data
public class AlipaySettleRuleReqDto {
/**
* 【必填】
* 默认结算类型
* 可选值有bankCard/alipayAccount。bankCard表示结算到银行卡alipayAccount表示结算到支付宝账号
*/
@JSONField(name = "default_settle_type")
private String defaultSettleType;
/**
* 【必填】
* 默认结算目标
* 当默认结算类型为bankCard时填写银行卡卡号其值需在进件填写的结算银行卡范围内当默认结算类型为alipayAccount时填写支付宝账号登录号其值需在进件填写的结算支付宝账号范围内。
*/
@JSONField(name = "default_settle_target")
private String defaultSettleTarget;
}

View File

@@ -0,0 +1,148 @@
package com.czg.third.alipay.dto.entry;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* @author yjjie
* @date 2026/1/4 11:31
*/
@Data
public class AlipaySitesReqDto {
/**
* 【必填】
* 网站类型
* 【枚举值】
* 网站: 01
* APP: 02
* 服务窗: 03
* 公众号: 04
* 其他: 05
* 支付宝小程序: 06
* 手机网站/H5: 07
*/
@JSONField(name = "site_type")
private String siteType;
/**
* 【选填】
* 站点地址
* 当传入service且包含jsapi支付时sites的site_type=06, site_url必填
*/
@JSONField(name = "site_url")
private String siteUrl;
/**
* 【选填】
* 站点名称
* 当传入service且包含jsapi支付、小程序支付时sites的site_type=06, site_name必填
*/
@JSONField(name = "site_name")
private String siteName;
/**
* 【选填】
* 截图照片
* 当传入交易场景trade_scene且当传入trade_scene=WAP、trade_scene=APP、trade_scene=PC时该参数必传
*/
@JSONField(name = "screenshot_image")
private String screenshotImage;
/**
* 【选填】
* 小程序appId
* 当传入service且包含jsapi支付时sites的site_type=06, tiny_app_id必填。
*/
@JSONField(name = "tiny_app_id")
private String tinyAppId;
/**
* 【选填】
* 测试账号
*/
@JSONField(name = "account")
private String account;
/**
* 【选填】
* 测试账号密码
*/
@JSONField(name = "password")
private String password;
/**
* 【选填】
* 上架状态
* 【枚举值】
* 已上线: ONLINE
* 已上线-内部: ONLINE_INNER
* 未上线: OFFLINE
*/
@JSONField(name = "status")
private String status;
/**
* 【选填】
* 授权函照片
*/
@JSONField(name = "auth_letter_image")
private String authLetterImage;
/**
* 【选填】
* 备注说明
*/
@JSONField(name = "remark")
private String remark;
/**
* 【选填】
* 备注说明图片
*/
@JSONField(name = "remark_image")
private String remarkImage;
/**
* 【选填】
* 网站域名
*/
@JSONField(name = "site_domain")
private String siteDomain;
/**
* 【选填】
* ICP备案主体信息服务名称
*/
@JSONField(name = "icp_service_name")
private String icpServiceName;
/**
* 【选填】
* ICP备案/许可证号
*/
@JSONField(name = "icp_no")
private String icpNo;
/**
* 【选填】
* ICP备案主体主办单位名称
*/
@JSONField(name = "icp_org_name")
private String icpOrgName;
/**
* 【选填】
* 下载地址
*/
@JSONField(name = "download")
private String download;
/**
* 【选填】
* 应用市场
* 【示例值】豌豆荚
*/
@JSONField(name = "market")
private String market;
}

View File

@@ -0,0 +1,50 @@
package com.czg.third.wechat;
import com.czg.third.wechat.dto.config.WechatPayConfigDto;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAPublicKeyConfig;
import com.wechat.pay.java.service.file.FileUploadService;
/**
* 微信支付 配置
*
* @author yjjie
* @date 2025/12/26 09:33
*/
public class WechatConfig {
/**
* 获取微信支付配置
* @param dto 微信支付配置
* @return 微信支付配置
*/
public static Config getRsaConfig(WechatPayConfigDto dto) {
if (dto == null) {
dto = WechatPayConfigDto.getDefaultConfig();
}
return new RSAPublicKeyConfig.Builder()
.merchantId(dto.getMerchantId())
.privateKey(dto.getPrivateKey())
.publicKey(dto.getPublicKey())
.publicKeyId(dto.getPublicKeyId())
.merchantSerialNumber(dto.getSerialNumber())
.apiV3Key(dto.getApiV3Key())
.build();
}
public static FileUploadService getFileUploadServiceByConfig(Config config) {
return new FileUploadService.Builder()
.config(config)
.build();
}
/**
* 获取文件上传服务
* @param dto 微信支付配置
* @return 文件上传服务
*/
public static FileUploadService getFileUploadService(WechatPayConfigDto dto) {
return getFileUploadServiceByConfig(getRsaConfig(dto));
}
}

View File

@@ -0,0 +1,77 @@
package com.czg.third.wechat;
import lombok.extern.slf4j.Slf4j;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.Base64;
/**
* 微信支付加解密
* @author yjjie
* @date 2025/12/26 10:58
*/
@Slf4j
public class WechatEncrypt {
public static String rsaEncryptOaep(String message, PublicKey publicKey) {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] data = message.getBytes(StandardCharsets.UTF_8);
byte[] cipherData = cipher.doFinal(data);
return Base64.getEncoder().encodeToString(cipherData);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
log.error("当前Java环境不支持RSA v1.5/OAEP");
} catch (InvalidKeyException e) {
log.error("无效的证书");
} catch (IllegalBlockSizeException | BadPaddingException e) {
log.error("加密原串的长度不能超过214字节");
}
return "";
}
public static String getFileBytesSha256(byte[] input) throws NoSuchAlgorithmException {
return bytesToHex(calculateSha256(input));
}
/**
* 核心方法:计算字节数组的 SHA-256 哈希值
* @param input 待计算的字节数组
* @return SHA-256 哈希值32 字节的字节数组)
*/
private static byte[] calculateSha256(byte[] input) throws NoSuchAlgorithmException {
// 获取 SHA-256 消息摘要实例Java 21 完全兼容)
MessageDigest digest = MessageDigest.getInstance("SHA-256");
// 计算哈希值
return digest.digest(input);
}
/**
* 辅助方法将字节数组转换为十六进制字符串SHA-256 结果常用格式)
* @param bytes 待转换的字节数组
* @return 十六进制字符串SHA-256 对应 64 位字符串)
*/
private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
// Java 21 可使用 var 简化声明
for (var b : bytes) {
// 将字节转换为两位十六进制数,不足补 0
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
}

View File

@@ -0,0 +1,418 @@
package com.czg.third.wechat;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import com.czg.PayCst;
import com.czg.dto.req.*;
import com.czg.dto.resp.EntryThirdRespDto;
import com.czg.dto.resp.QueryStatusResp;
import com.czg.exception.CzgException;
import com.czg.third.wechat.dto.config.WechatPayConfigDto;
import com.czg.third.wechat.dto.req.entry.*;
import com.czg.third.wechat.dto.req.entry.business.WechatEntryBusinessReqDto;
import com.czg.third.wechat.dto.req.entry.business.WechatEntryIdentityReqDto;
import com.czg.third.wechat.dto.req.entry.business.WechatEntryLicenseReqDto;
import com.czg.third.wechat.dto.req.entry.business.sales.WechatEntryMiniProgramReqDto;
import com.czg.third.wechat.dto.req.entry.business.sales.WechatEntrySalesInfoReqDto;
import com.czg.third.wechat.dto.req.entry.business.sales.WechatEntryStoreInfoReqDto;
import com.czg.third.wechat.dto.resp.WechatAuditDetail;
import com.czg.third.wechat.dto.resp.WechatQueryStateResp;
import com.czg.utils.UploadFileUtil;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.cipher.PrivacyEncryptor;
import com.wechat.pay.java.service.file.FileUploadService;
import com.wechat.pay.java.service.file.model.FileUploadResponse;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* 微信支付进件 管理
* 参考地址 <a href="https://pay.weixin.qq.com/doc/v3/partner/4012719997">...</a>
*
* @author yjjie
* @date 2025/12/26 10:57
*/
@Slf4j
public class WechatEntryManager {
/**
* 查询商户进件状态
*
* @param configDto 配置信息
* @param merchantCode 自系统的商户编号
* @return 进件状态
*/
public static QueryStatusResp queryMerchantEntryStatus(WechatPayConfigDto configDto, String merchantCode) {
QueryStatusResp queryStatusResp = new QueryStatusResp();
queryStatusResp.setPlatform(PayCst.Platform.WECHAT);
queryStatusResp.setMerchantCode(merchantCode);
String resp = WechatReqUtils.getReq(configDto, "/v3/applyment4sub/applyment/business_code/" + merchantCode, Map.of());
JSONObject object = JSONObject.parseObject(resp);
JSONObject data = object.getJSONObject("data");
if (data == null) {
log.error("微信查询进件状态失败:{}", resp);
queryStatusResp.setFailReason(object.getString("message"));
queryStatusResp.setStatus(PayCst.EntryStatus.REJECTED);
return queryStatusResp;
}
log.info("微信查询进件状态:{}", resp);
WechatQueryStateResp stateResp = JSONObject.parseObject(data.toJSONString(), WechatQueryStateResp.class);
queryStatusResp.setApplyId(stateResp.getApplyId());
switch (stateResp.getApplyState()) {
case "APPLYMENT_STATE_EDITTING" -> {
queryStatusResp.setStatus(PayCst.EntryStatus.WAIT);
queryStatusResp.setFailReason("");
}
case "APPLYMENT_STATE_AUDITING" -> {
queryStatusResp.setStatus(PayCst.EntryStatus.AUDIT);
queryStatusResp.setFailReason("");
}
case "APPLYMENT_STATE_REJECTED" -> {
queryStatusResp.setStatus(PayCst.EntryStatus.REJECTED);
StringBuilder msg = new StringBuilder();
for (WechatAuditDetail auditDetail : stateResp.getAuditDetail()) {
msg.append(auditDetail.getRejectReason()).append("");
}
queryStatusResp.setFailReason(msg.toString());
}
case "APPLYMENT_STATE_TO_BE_CONFIRMED", "APPLYMENT_STATE_TO_BE_SIGNED" -> {
queryStatusResp.setStatus(PayCst.EntryStatus.SIGN);
queryStatusResp.setFailReason("");
queryStatusResp.setSignUrl(stateResp.getSignUrl());
}
case "APPLYMENT_STATE_SIGNING", "APPLYMENT_STATE_FINISHED" -> {
queryStatusResp.setStatus(PayCst.EntryStatus.FINISH);
queryStatusResp.setFailReason("");
queryStatusResp.setThirdMerchantId(stateResp.getSubMchId());
}
}
return queryStatusResp;
}
/**
* 进件商户
*
* @param configDto 配置信息
* @param reqDto 请求信息
*/
public static EntryThirdRespDto entryMerchant(WechatPayConfigDto configDto, AggregateMerchantDto reqDto) {
WechatEntryReqDto entryReqDto = buildEntryParams(configDto, reqDto);
EntryThirdRespDto respDto = new EntryThirdRespDto()
.setPlatform(PayCst.Platform.WECHAT);
log.info("微信进件参数:{}", JSONObject.toJSONString(entryReqDto));
try {
String params = JSONObject.toJSONString(entryReqDto, JSONWriter.Feature.IgnoreEmpty);
String respBody = WechatReqUtils.postReq(configDto, "/v3/applyment4sub/applyment/", params);
JSONObject object = JSONObject.parseObject(respBody);
JSONObject data = object.getJSONObject("data");
log.info("微信进件结果:{}", respBody);
if (data != null) {
respDto.setStatus(PayCst.EntryStatus.INIT);
respDto.setEntryId(data.getString("applyment_id"));
respDto.setErrorMsg("");
} else {
respDto.setStatus(PayCst.EntryStatus.REJECTED);
respDto.setEntryId("");
respDto.setErrorMsg(object.getString("message"));
}
} catch (Exception e) {
log.error("微信进件报错:{}", e.getMessage(), e);
respDto.setStatus(PayCst.EntryStatus.REJECTED);
respDto.setEntryId("");
respDto.setErrorMsg(e.getMessage());
}
return respDto;
}
public static JSONObject queryBankList(WechatPayConfigDto configDto, Integer offset, Integer limit) {
String resp = WechatReqUtils.getReq(configDto, "/v3/capital/capitallhh/banks/corporate-banking", Map.of("offset", offset, "limit", limit));
log.info("微信查询银行列表:{}", resp);
return JSONObject.parseObject(resp);
}
public static JSONObject queryBankBranchList(WechatPayConfigDto configDto, String bankAliceCode, String cityCode, Integer offset, Integer limit) {
String resp = WechatReqUtils.getReq(configDto, "/v3/capital/capitallhh/banks/" + bankAliceCode + "/branches", Map.of("city_code", cityCode, "offset", offset, "limit", limit));
log.info("微信查询银行支行列表:{}", resp);
return JSONObject.parseObject(resp);
}
public static JSONObject queryProvinceList(WechatPayConfigDto configDto) {
String resp = WechatReqUtils.getReq(configDto, "/v3/capital/capitallhh/areas/provinces", Map.of());
log.info("微信查询省份列表:{}", resp);
return JSONObject.parseObject(resp);
}
public static JSONObject queryCityList(WechatPayConfigDto configDto, String provinceCode) {
String resp = WechatReqUtils.getReq(configDto, "/v3/capital/capitallhh/areas/provinces/" + provinceCode + "/cities", Map.of());
log.info("微信查询城市列表:{}", resp);
return JSONObject.parseObject(resp);
}
/**
* 上传图片
*
* @param url 图片URL
* @return 图片ID
*/
public static String uploadImage(WechatPayConfigDto configDto, String url) {
if (configDto == null) {
configDto = WechatPayConfigDto.getDefaultConfig();
}
// 校验入参
if (url == null || url.trim().isEmpty()) {
log.error("上传图片失败URL参数为空");
return "";
}
FileUploadService service = WechatConfig.getFileUploadService(configDto);
try {
// 获取图片字节数组
byte[] bytes = UploadFileUtil.downloadImage(url);
if (bytes.length == 0) {
log.error("下载的图片内容为空URL{}", url);
return "";
}
// 2. 计算SHA256
String sha256Hex = WechatEncrypt.getFileBytesSha256(bytes);
// 3. 构造元数据从URL提取文件名或自定义
JSONObject meta = new JSONObject();
meta.put("sha256", sha256Hex);
// 从URL提取文件名若提取失败则使用默认名
String fileName = UploadFileUtil.extractFileNameFromUrl(url);
meta.put("filename", fileName);
// 4. 上传图片到微信接口
FileUploadResponse uploadResponse = service.uploadImage(
configDto.getDomain() + "/v3/merchant/media/upload",
meta.toJSONString(),
fileName,
bytes
);
return uploadResponse.getMediaId();
} catch (Exception e) {
log.error("微信上传图片报错URL{},错误信息:{}", url, e.getMessage(), e);
}
return "";
}
/**
* 构建进件参数
*
* @param reqDto 请求参数
* @return 进件参数
*/
private static WechatEntryReqDto buildEntryParams(WechatPayConfigDto configDto, AggregateMerchantDto reqDto) {
WechatEntryReqDto entryParams = new WechatEntryReqDto();
Config config = WechatConfig.getRsaConfig(configDto);
PrivacyEncryptor encryptor = config.createEncryptor();
entryParams.setBusinessCode(reqDto.getMerchantCode());
MerchantBaseInfoDto baseInfo = reqDto.getMerchantBaseInfo();
LegalPersonInfoDto legalPersonInfo = reqDto.getLegalPersonInfo();
BusinessLicenceInfoDto businessLicenceInfo = reqDto.getBusinessLicenceInfo();
StoreInfoDto storeInfo = reqDto.getStoreInfo();
SettlementInfoDto settlementInfo = reqDto.getSettlementInfo();
WechatEntryContactReqDto contactInfo = new WechatEntryContactReqDto();
contactInfo.setContactType(baseInfo.getContactPersonType());
// 默认都使用身份证 暂不支持其他证件
contactInfo.setContactIdDocCopy("IDENTIFICATION_TYPE_IDCARD");
if (PayCst.ContactPersonType.SUPER.equals(baseInfo.getContactPersonType())) {
contactInfo.setContactName(encryptor.encrypt(baseInfo.getContactName()));
contactInfo.setContactIdNumber(encryptor.encrypt(baseInfo.getContactPersonId()));
contactInfo.setContactIdDocCopy(baseInfo.getContactIdCardFrontPic().getWechatId());
contactInfo.setContactIdDocCopyBack(baseInfo.getContactIdCardBackPic().getWechatId());
contactInfo.setContactPeriodBegin(baseInfo.getContactPersonIdStartDate());
contactInfo.setContactPeriodEnd(baseInfo.getContactPersonIdEndDate());
contactInfo.setMobilePhone(encryptor.encrypt(baseInfo.getContactPhone()));
contactInfo.setContactEmail(encryptor.encrypt(baseInfo.getContactEmail()));
} else if (PayCst.ContactPersonType.LEGAL.equals(baseInfo.getContactPersonType())) {
contactInfo.setContactName(encryptor.encrypt(legalPersonInfo.getLegalPersonName()));
contactInfo.setContactIdNumber(encryptor.encrypt(legalPersonInfo.getLegalPersonId()));
contactInfo.setContactIdDocCopy(legalPersonInfo.getIdCardFrontPic().getWechatId());
contactInfo.setContactIdDocCopyBack(legalPersonInfo.getIdCardBackPic().getWechatId());
contactInfo.setContactPeriodBegin(legalPersonInfo.getLegalIdPersonStartDate());
contactInfo.setContactPeriodEnd(legalPersonInfo.getLegalPersonIdEndDate());
contactInfo.setMobilePhone(encryptor.encrypt(legalPersonInfo.getLegalPersonPhone()));
contactInfo.setContactEmail(encryptor.encrypt(legalPersonInfo.getLegalPersonEmail()));
} else {
throw new CzgException("联系人类型错误");
}
entryParams.setContactInfo(contactInfo);
WechatEntrySubjectReqDto subjectInfo = new WechatEntrySubjectReqDto();
if ("0".equals(baseInfo.getUserType())) {
subjectInfo.setSubjectType("SUBJECT_TYPE_INDIVIDUAL");
} else if ("1".equals(baseInfo.getUserType())) {
switch (baseInfo.getCompanyChildType()) {
case "1" -> subjectInfo.setSubjectType("SUBJECT_TYPE_ENTERPRISE");
case "2" -> subjectInfo.setSubjectType("SUBJECT_TYPE_INSTITUTIONS");
case "3" -> subjectInfo.setSubjectType("SUBJECT_TYPE_GOVERNMENT");
case "4" -> subjectInfo.setSubjectType("SUBJECT_TYPE_OTHERS");
default -> throw new CzgException("主体类型错误");
}
} else {
throw new CzgException("用户类型错误");
}
subjectInfo.setFinanceInstitution(false);
WechatEntryLicenseReqDto licenseReqDto = new WechatEntryLicenseReqDto();
licenseReqDto.setLicenseCopy(businessLicenceInfo.getLicensePic().getWechatId());
licenseReqDto.setLicenseNumber(businessLicenceInfo.getLicenceNo());
licenseReqDto.setMerchantName(businessLicenceInfo.getLicenceName());
licenseReqDto.setLegalPerson(legalPersonInfo.getLegalPersonName());
licenseReqDto.setLicenseAddress(businessLicenceInfo.getRegisteredAddress());
licenseReqDto.setPeriodBegin(businessLicenceInfo.getLicenceStartDate());
licenseReqDto.setPeriodEnd(businessLicenceInfo.getLicenceEndDate());
subjectInfo.setBusinessLicenseInfo(licenseReqDto);
WechatEntryIdentityReqDto identityInfo = new WechatEntryIdentityReqDto();
identityInfo.setIdHolderType(PayCst.ContactPersonType.LEGAL);
subjectInfo.setIdentityInfo(identityInfo);
entryParams.setSubjectInfo(subjectInfo);
WechatEntryBusinessReqDto businessReqInfo = new WechatEntryBusinessReqDto();
businessReqInfo.setMerchantShortname(baseInfo.getShortName());
businessReqInfo.setServicePhone(PayCst.ContactPersonType.LEGAL.equals(baseInfo.getContactPersonType()) ? legalPersonInfo.getLegalPersonPhone() : baseInfo.getContactPhone());
WechatEntrySalesInfoReqDto salesInfo = new WechatEntrySalesInfoReqDto();
salesInfo.setSalesScenesType(List.of("SALES_SCENES_STORE", "SALES_SCENES_MINI_PROGRAM"));
WechatEntryStoreInfoReqDto bizStoreInfo = new WechatEntryStoreInfoReqDto();
bizStoreInfo.setBizStoreName(baseInfo.getShortName());
bizStoreInfo.setBizAddressCode(storeInfo.getMercAreaCode());
bizStoreInfo.setBizStoreAddress(storeInfo.getBusinessAddress());
bizStoreInfo.setStoreEntrancePic(List.of(storeInfo.getDoorPic().getWechatId()));
bizStoreInfo.setIndoorPic(List.of(storeInfo.getInsidePic().getWechatId(), storeInfo.getCashierDeskPic().getWechatId()));
salesInfo.setBizStoreInfo(bizStoreInfo);
WechatEntryMiniProgramReqDto miniProgramInfo = new WechatEntryMiniProgramReqDto();
miniProgramInfo.setMiniProgramAppid("wxd88fffa983758a30");
salesInfo.setMiniProgramInfo(miniProgramInfo);
businessReqInfo.setSalesInfo(salesInfo);
entryParams.setBusinessInfo(businessReqInfo);
WechatEntrySettleReqDto settlementReqInfo = new WechatEntrySettleReqDto();
settlementReqInfo.setSettlementId("0".equals(baseInfo.getUserType()) ? "719" : "716");
settlementReqInfo.setQualificationType("餐饮");
settlementReqInfo.setActivitiesId("20191030111cff5b5e");
settlementReqInfo.setActivitiesRate("0.38");
entryParams.setSettlementInfo(settlementReqInfo);
WechatEntryBankAccountReqDto bankAccountReqInfo = new WechatEntryBankAccountReqDto();
bankAccountReqInfo.setBankAccountType("21".equals(settlementInfo.getSettlementCardType()) ? "BANK_ACCOUNT_TYPE_CORPORATE" : "BANK_ACCOUNT_TYPE_PERSONAL");
bankAccountReqInfo.setAccountName(encryptor.encrypt(settlementInfo.getSettlementName()));
bankAccountReqInfo.setAccountBank(settlementInfo.getBankName());
bankAccountReqInfo.setBankBranchId(settlementInfo.getBankBranchCode());
bankAccountReqInfo.setBankName(settlementInfo.getBankBranchName());
bankAccountReqInfo.setAccountNumber(encryptor.encrypt(settlementInfo.getSettlementCardNo()));
entryParams.setBankAccountInfo(bankAccountReqInfo);
return entryParams;
}
public static void main(String[] args) throws IOException {
queryMerchantEntryStatus(null, "20220106000000000001");
// int offset = 0;
// Integer limit = 100;
// JSONObject resp = queryBankList(null, offset, limit);
// queryBankBranchList(dto, "1000009561", "110000", offset, limit);
// queryBankBranchList(dto, "1000009561", "29", offset, limit);
// queryProvinceList(dto);
// queryCityList(dto, "28");
// String string = uploadImage(dto, "https://czg-qr-order.oss-cn-beijing.aliyuncs.com/indexs/shuangbackground.png");
// log.info("图片上传成功:{}", string);
// Config config = WechatConfig.getRsaConfig(dto);
// PrivacyEncryptor encryptor = config.createEncryptor();
//
// WechatEntryReqDto reqDto = new WechatEntryReqDto()
// .setBusinessCode("MER_20231025110010000010000000000001");
//
// WechatEntryContactReqDto contactInfo = new WechatEntryContactReqDto()
// .setContactType("LEGAL")
// .setContactName(encryptor.encrypt("张三"))
// .setContactIdType("IDCARD")
// .setContactIdNumber(encryptor.encrypt("110101199001011234"))
// .setContactIdDocCopy("https://czg-qr-order.oss-cn-beijing.aliyuncs.com/indexs/shuangbackground.png")
// .setContactIdDocCopyBack("https://czg-qr-order.oss-cn-beijing.aliyuncs.com/indexs/shuangbackground.png")
// .setContactPeriodBegin("2023-10-25")
// .setContactPeriodEnd("2024-10-25")
// .setMobilePhone(encryptor.encrypt("13888888888"))
// .setContactEmail(encryptor.encrypt("123456@qq.com"));
//
// reqDto.setContactInfo(contactInfo);
//
// WechatEntrySubjectReqDto subjectInfo = new WechatEntrySubjectReqDto()
// .setSubjectType("SUBJECT_TYPE_INDIVIDUAL");
//
// WechatEntryLicenseReqDto licenseInfo = new WechatEntryLicenseReqDto()
// .setLicenseCopy("https://czg-qr-order.oss-cn-beijing.aliyuncs.com/indexs/shuangbackground.png")
// .setLicenseNumber("110101199001011234")
// .setMerchantName("张三商行")
// .setLegalPerson(encryptor.encrypt("张三"))
// .setLicenseAddress("北京")
// .setPeriodBegin("2023-10-25")
// .setPeriodEnd("2024-10-25");
// subjectInfo.setBusinessLicenseInfo(licenseInfo);
//
// WechatEntryIdentityReqDto identityInfo = new WechatEntryIdentityReqDto()
// .setIdHolderType("LEGAL");
// subjectInfo.setIdentityInfo(identityInfo);
//
// reqDto.setSubjectInfo(subjectInfo);
//
// WechatEntryBusinessReqDto businessInfo = new WechatEntryBusinessReqDto()
// .setMerchantShortname("张三商行")
// .setServicePhone("13888888888");
// WechatEntrySalesInfoReqDto salesInfo = new WechatEntrySalesInfoReqDto()
// .setSalesScenesType(List.of("SALES_SCENES_STORE"));
// WechatEntryStoreInfoReqDto storeInfo = new WechatEntryStoreInfoReqDto()
// .setBizStoreName("张三商行")
// .setBizAddressCode("110101")
// .setBizStoreAddress("北京")
// .setStoreEntrancePic(List.of("https://czg-qr-order.oss-cn-beijing.aliyuncs.com/indexs/shuangbackground.png"))
// .setIndoorPic(List.of("https://czg-qr-order.oss-cn-beijing.aliyuncs.com/indexs/shuangbackground.png"));
// salesInfo.setBizStoreInfo(storeInfo);
// businessInfo.setSalesInfo(salesInfo);
//
// reqDto.setBusinessInfo(businessInfo);
//
// WechatEntrySettleReqDto settleInfo = new WechatEntrySettleReqDto()
// .setSettlementId("719")
// .setQualificationType("IDCARD")
// .setQualifications(List.of("110101199001011234"))
// .setActivitiesId("20191030111cff5b5e");
//
// reqDto.setSettlementInfo(settleInfo);
//
// WechatEntryBankAccountReqDto bankAccountInfo = new WechatEntryBankAccountReqDto()
// .setBankAccountType("BANK_ACCOUNT_TYPE_CORPORATE")
// .setAccountBank("ICBC")
// .setBankName("中国工商银行")
// .setAccountName(encryptor.encrypt("张三"))
// .setAccountNumber(encryptor.encrypt("110101199001011234"));
//
// reqDto.setBankAccountInfo(bankAccountInfo);
// entryMerchant(dto, reqDto);
}
}

View File

@@ -0,0 +1,11 @@
package com.czg.third.wechat;
/**
* @author yjjie
* @date 2025/12/26 09:15
*/
public class WechatPayManager {
public static void main(String[] args) {
}
}

View File

@@ -0,0 +1,112 @@
package com.czg.third.wechat;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.czg.exception.CzgException;
import com.czg.third.wechat.dto.config.WechatPayConfigDto;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.cipher.Signer;
import lombok.extern.slf4j.Slf4j;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* 微信请求工具类
*
* @author yjjie
* @date 2025/12/29 10:02
*/
@Slf4j
public class WechatReqUtils {
public static String postReq(WechatPayConfigDto configDto, String url, String body) {
return req(configDto, url, "POST", body);
}
public static String getReq(WechatPayConfigDto configDto, String url, Map<String, Object> params) {
url = Arrays.stream(params.entrySet().toArray(new Map.Entry[0]))
.map(entry -> entry.getKey() + "=" + URLEncoder.encode(entry.getValue().toString(), StandardCharsets.UTF_8))
.collect(Collectors.joining("&", url + "?", ""));
// 如果最后没有参数,则去掉多余的 "?"
url = url.endsWith("?") ? url.substring(0, url.length() - 1) : url;
return req(configDto, url, "GET", "");
}
private static String req(WechatPayConfigDto configDto, String url, String method, String body) {
if (configDto == null) {
configDto = WechatPayConfigDto.getDefaultConfig();
}
long timestamp = getTimestamp();
String nonce = getNonceStr();
String signature = encryptReqParam(configDto, method, url, body, timestamp, nonce);
String authorization = String.format("WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",signature=\"%s\",timestamp=\"%d\",serial_no=\"%s\"",
configDto.getMerchantId(), nonce, signature, timestamp, configDto.getSerialNumber());
HttpRequest request = switch (method) {
case "POST" -> HttpUtil.createPost(configDto.getDomain() + url)
.header("Authorization", authorization)
.header("Content-Type", "application/json")
.header("Wechatpay-Serial", configDto.getPublicKeyId())
.body(body);
case "GET" -> HttpUtil.createGet(configDto.getDomain() + url)
.header("Authorization", authorization)
.header("Content-Type", "application/json")
.header("Wechatpay-Serial", configDto.getPublicKeyId());
default -> throw new CzgException("不支持的请求方法");
};
HttpResponse response = request.execute();
String s = response.body();
log.info("微信支付请求url = {}, method: {}, body: {}, resp: {}", url, method, body, s);
return s;
}
/**
* 加密请求参数
*
* @param configDto 配置
* @param method 请求方法
* @param url 请求地址
* @param body 请求报文主体
* @return 加密后的报文
* <p>
* 签名方法
* HTTP请求方法\n
* URL\n
* 请求时间戳\n
* 请求随机串\n
* 请求报文主体\n
*/
public static String encryptReqParam(WechatPayConfigDto configDto, String method, String url, String body, long timestamp, String nonce) {
String encryptStr = String.format("%s\n%s\n%d\n%s\n%s\n",
method, url, timestamp, nonce, body);
Config config = WechatConfig.getRsaConfig(configDto);
Signer signer = config.createSigner();
String signature = signer.sign(encryptStr).getSign();
log.info("微信签名 encryptStr = {},\nsignature{}", encryptStr, signature);
return signature;
}
/**
* 获取随机串
*/
private static String getNonceStr() {
return UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
}
/**
* 获取时间戳
*/
private static long getTimestamp() {
return System.currentTimeMillis() / 1000;
}
}

View File

@@ -0,0 +1,97 @@
package com.czg.third.wechat.dto.config;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @author yjjie
* @date 2025/12/26 09:21
*/
@Data
@Accessors(chain = true)
public class WechatPayConfigDto {
/**
* 商户号
*/
private String merchantId;
/**
* Api V3密钥
*/
private String apiV3Key;
/**
* 商户证书序列号
*/
private String serialNumber;
/**
* 商户私钥
*/
private String privateKey;
/**
* 商户公钥
*/
private String publicKey;
/**
* 商户公钥 ID
*/
private String publicKeyId;
/**
* 微信支付域名
* <a href="https://api.mch.weixin.qq.com"></a>
*/
private String domain;
public static WechatPayConfigDto getDefaultConfig() {
return new WechatPayConfigDto()
.setMerchantId("1643779408")
.setApiV3Key("a92baac5eb7a36ed8ec198113e769a03")
.setSerialNumber("4DE9BAC2EA584C3F274F694C9753CA814C4E9BF4")
.setPublicKey("""
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtbeWXEEjBaYtw2OyM+SC
aCEMbryRXi4duKxx3vYG4rUVix+d5/Jz7Khev4Upml9zC+Xxvv/G9bHWAUyolzqD
wefahGurIxr43r4GJVnQ4i5G6BbBVw5d4Vuz0y/9Zd14zmc/EmBpT0Ml26H7tOZl
n1LSbQ4xNFkrRKrNEcExBLxkCd+K5K2TREZznywIi0izbHImvuzM8IneuR51FiqK
pdFnAjTwb126EIj6ECkL6KLCl8RWqpfiX8SFiolSQLs1/w79O0sIUk96X2zWpnoW
rTDFatPif/VEKl3y2dTlxxDxoZtVi48yTDW00OMzVl5D67oX3FVK0KsjHJSCfGlZ
6wIDAQAB
-----END PUBLIC KEY-----""")
.setPrivateKey("""
-----BEGIN PRIVATE KEY-----
MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDqnAZhTxT572fo
6wvSr8Rt0vRXg+EFKC6UvUiNE24oocQU5fjedX9KL/+fmoqay/xqIxvxvCNFTs4J
zlMqavSl6bMWCpjvf/Ry82JmN1v2kJEO4lgP8BsEiqlUpObCH8BMAVUOn1j+9q4Q
uZJJcbtRvec2fNweDM8EJp4B7RlUdDbHm86lfcDVp8iini7VjDp6D7aHT+C8A8OT
ugxQIquDec778wVd9r2Sv3+t6rAzFs+n+Zu++2xtFEPhO5N0wjrLHaukl+9crU+1
lktjDzcPd07SwGZ+A+3BTmW3UCramI3506e/3MWBECB7ge+gM4URAV0nJJCLH/Im
WgEvPMr5AgMBAAECggEBAKv+wraoMWqiVv1tA/froAgbtcJLDranJK8qrXuvmPz0
yzm+91qvrSgIVFEADUk67swo/R2Vng37nhWWS2Y3jy/rSr2H+2Lp3Z5ATA0/3I3A
onfU/FaC4mvL9CP32KzMdj/CYkccDzSsSCQ+x75MQNXGcTGDDCSDo2kZnpEu73j3
aqvO1jbqTGWigRfjOIaIhStjQIT8nEm/3mJ4f5dM9M6FMz33mhax8EahSgvdahYB
t45iaGOWw81OJhmry47EvpwjXBl7jtO2HX3LiLbq5Ebcwu1zqDz5NM7ttnnGAqWC
6y7JN5Vt4wPYrCydiUDe7dj0looffr2yw6MkNfYjLGECgYEA+FAvbEInQEi9YguS
CQtLHngqvYeai66tvyykog9o38KHnPGx2Myf+rn/Hcp7KNRfjd5G34CCNg7KLnrx
YIYh6+2bY3jirzdYUxuNKGbvM4gky/6M/P9zHF/uALKOE02yArdqemf4UxUvrYCc
JdXsAMqvbpdvW1aGgNRB32YCkG0CgYEA8d89vawsCjNCEUh0VWTMoBLFoex3qBMZ
rfzYQeBo6bDCRlAlUVzl+ouBUxSYxP/U8rzeNaRzGUwRLmlGMjyIr58FBlHsg2cR
DlsX3HVCUjpS6sgPXOqakdiLfhMcHZAspislSyVfeS3TbUWiA45+5PuNUq+EZYwl
ESsB1+yfRT0CgYEA0Ct49ksnWM8iZbXJgeeD3FFlk2rBd2TDqEem5W4Bv8T3p+0/
6b7yR2HyrGj5gys3yFmWFP1JLESN3xWWkhMhEQcrg+LuN3Iwi8vHNR3GXu892f7W
96q4OAt8Hf2S+j/igkB99YyANDbIt63gOh/zMF67X/14j5wkOpC3gK+maqkCgYEA
s7nIrPoUt3ejLiiCmTmPe5q3VDzcJP4cZNau8zSHgK6hjZHcSPsYwPWMoWl6o1fe
qoiBLacHB9MoKS58xLOKdcVZ/Ho/ntylJd+2eVCAeY1xM5h5IfgJ5znbXVFh4O3S
357L1Wzt5qOQqW/GlZH65LevKbPWU4axvHISqpnfN5kCgYEAqiqLuAPu84VSsqsE
GFh25t+1f0JY1sNfilE3/t9AdQeeCFv/5z9KB1kMt3a5ZFeVonsFIvCyaOJjhSkj
4HCB94vWS7NuN5G9r874lMaPbZYQGwrcVaf265tN7cYYr3gUx1qB6+u+fh/kcft1
BBmTzhb0zp5k8ngwAkA1g/LK204=
-----END PRIVATE KEY-----""")
.setPublicKeyId("PUB_KEY_ID_0116437794082025111000382377001000")
.setDomain("https://api.mch.weixin.qq.com");
}
}

View File

@@ -0,0 +1,51 @@
package com.czg.third.wechat.dto.req.entry;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 微信进件-补充信息
*
* @author yjjie
* @date 2025/12/26 13:43
*/
@Data
@Accessors(chain = true)
public class WechatEntryAdditionReqDto {
/**
* 【选填】
* 法定代表人开户承诺函
* 模板下载地址 <a href="https://kf.qq.com/faq/191018yUFjEj191018Vfmaei.html">...</a>
* 通过图片上传完成后 MediaID
*/
@JSONField(name = "legal_person_commitment")
private String legalPersonCommitment;
/**
* 【选填】
* 法定代表人开户意愿视频
* 通过视频上传完成后 MediaID
*/
@JSONField(name = "legal_person_video")
private String legalPersonVideo;
/**
* 【选填】
* 补充材料
* 最多可上传5张照片
* 通过图片上传完成后 MediaID
*/
@JSONField(name = "business_addition_pics")
private List<String> businessAdditionPics;
/**
* 【选填】
* 补充说明 512字以内
*/
@JSONField(name = "business_addition_msg")
private String businessAdditionMsg;
}

View File

@@ -0,0 +1,75 @@
package com.czg.third.wechat.dto.req.entry;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 微信进件-银行账号
*
* @author yjjie
* @date 2025/12/26 13:42
*/
@Data
@Accessors(chain = true)
public class WechatEntryBankAccountReqDto {
/**
* 【必填】
* 账户类型
* 1、若主体为企业/政府机关/事业单位/社会组织,可填写:对公银行账户;
* 2、若主体为个体户可选择填写对公银行账户或经营者个人银行卡
* 可选取值 BANK_ACCOUNT_TYPE_CORPORATE: 对公银行账户 BANK_ACCOUNT_TYPE_PERSONAL: 经营者个人银行卡
*/
@JSONField(name = "bank_account_type")
private String bankAccountType;
/**
* 【必填】
* 开户名称
* 1、选择“经营者个人银行卡”时开户名称必须与“经营者证件姓名”一致
* 2、选择“对公银行账户”时开户名称必须与营业执照上的“商户名称”一致
* 3、该字段需要使用微信支付公钥加密推荐请参考获取微信支付公钥ID说明以及微信支付公钥加密敏感信息指引也可以使用微信支付平台证书公钥加密参考获取平台证书序列号、平台证书加密敏感信息指引。
* <a href="https://pay.weixin.qq.com/doc/v3/partner/4013059044">...</a>
*/
@JSONField(name = "account_name")
private String accountName;
/**
* 【必填】
* 开户银行
* 对私银行调用查询支持个人业务的银行列表API
* 对公银行调用查询支持对公业务的银行列表API
*/
@JSONField(name = "account_bank")
private String accountBank;
/**
* 【选填】
* 开户银行银行号
* 1、根据开户银行查询接口中的“是否需要填写支行”判断是否需要填写。如为其他银行开户银行全称含支行和开户银行联行号二选一
* 2、详细需调用查询支行列表API查看查询结果。 <a href="https://pay.weixin.qq.com/doc/v3/partner/4012697673">...</a>
*/
@JSONField(name = "bank_branch_id")
private String bankBranchId;
/**
* 【选填】
* 开户银行全称(含支行)
* 1、根据开户银行查询接口中的“是否需要填写支行”判断是否需要填写。如为其他银行开户银行全称含支行和开户银行联行号二选一
* 2、详细需调用查询支行列表API查看查询结果。 <a href="https://pay.weixin.qq.com/doc/v3/partner/4012697673">...</a>
*/
@JSONField(name = "bank_name")
private String bankName;
/**
* 【必填】
* 开户银行账号
* 1、选择“经营者个人银行卡”时开户账号为经营者个人银行卡号
* 2、选择“对公银行账户”时开户账号为对公银行账号
* 3、该字段需要使用微信支付公钥加密推荐请参考获取微信支付公钥ID说明以及微信支付公钥加密敏感信息指引也可以使用微信支付平台证书公钥加密参考获取平台证书序列号、平台证书加密敏感信息指引。
* <a href="https://pay.weixin.qq.com/doc/v3/partner/4013059044">...</a>
*/
@JSONField(name = "account_number")
private String accountNumber;
}

View File

@@ -0,0 +1,139 @@
package com.czg.third.wechat.dto.req.entry;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 进件 联系人信息
*
* @author yjjie
* @date 2025/12/26 13:38
*/
@Data
@Accessors(chain = true)
public class WechatEntryContactReqDto {
/**
* 【必填】
* 超级管理员类型
* 主体为“个体工商户/企业/政府机关/事业单位/社会组织”可选择LEGAL经营者/法定代表人SUPER经办人 。(经办人:经商户授权办理微信支付业务的人员)。
* 可选取值
* LEGAL: 经营者/法定代表人
* SUPER: 经办人
*/
@JSONField(name = "contact_type")
private String contactType;
/**
* 【必填】
* 超级管理员姓名
* 1、长度为2-100个字符
* 2、前后不能有空格、制表符、换行符
* 3、不能仅含数字、特殊字符
* 4、仅能填写数字、英文字母、汉字及特殊字符。
* 该字段需要使用微信支付公钥加密推荐请参考获取微信支付公钥ID说明以及微信支付公钥加密敏感信息指引也可以使用微信支付平台证书公钥加密参考获取平台证书序列号、平台证书加密敏感信息指引。
*/
@JSONField(name = "contact_name")
private String contactName;
/**
* 【选填】
* 超级管理员证件类型 当超级管理员类型是经办人时,请上传超级管理员证件类型。
* 可选取值
* IDENTIFICATION_TYPE_IDCARD: 中国大陆居民-身份证
* IDENTIFICATION_TYPE_OVERSEA_PASSPORT: 其他国家或地区居民-护照
* IDENTIFICATION_TYPE_HONGKONG_PASSPORT: 中国香港居民-来往内地通行证
* IDENTIFICATION_TYPE_MACAO_PASSPORT: 中国澳门居民-来往内地通行证
* IDENTIFICATION_TYPE_TAIWAN_PASSPORT: 中国台湾居民-来往大陆通行证
* IDENTIFICATION_TYPE_FOREIGN_RESIDENT: 外国人居留证
* IDENTIFICATION_TYPE_HONGKONG_MACAO_RESIDENT: 港澳居民居住证
* IDENTIFICATION_TYPE_TAIWAN_RESIDENT: 台湾居民居住证
*/
@JSONField(name = "contact_id_type")
private String contactIdType;
/**
* 【选填】
* 超级管理员身份证件号码
* 1、当超级管理员类型是经办人时请上传超级管理员证件号码
* 2、可传身份证、来往内地通行证、来往大陆通行证、护照等证件号码号码规范如下
* 身份证限中国大陆居民17位数字+1位数字|X
* 护照限境外人士4-15位 数字|字母|连字符;
* 中国香港居民--来往内地通行证H/h开头+8或10位数字/字母;
* 中国澳门居民--来往内地通行证M/m开头+8或10位数字/字母;
* 中国台湾居民--来往大陆通行证8位数字或10位数字
* 外国人居留证15位 数字|字母;
* 台湾居民居住证/港澳居民居住证17位数字+1位数字|X
* 3、超级管理员签约时校验微信号绑定的银行卡实名信息是否与该证件号码一致
* 4、该字段需要使用微信支付公钥加密推荐请参考获取微信支付公钥ID说明以及微信支付公钥加密敏感信息指引也可以使用微信支付平台证书公钥加密参考获取平台证书序列号、平台证书加密敏感信息指引。
*/
@JSONField(name = "contact_id_number")
private String contactIdNumber;
/**
* 【选填】
* 超级管理员证件正面照片
* 1、当超级管理员类型是经办人时请上传超级管理员证件的正面照片
* 2、若证件类型为身份证请上传人像面照片
* 3、正面拍摄、清晰、四角完整、无反光或遮挡不得翻拍、截图、镜像、PS
* 4、请上传彩色照片或彩色扫描件或复印件需加盖公章鲜章可添加“微信支付”相关水印如微信支付认证见【指引文档】
* 5、可上传1张图片请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "contact_id_doc_copy")
private String contactIdDocCopy;
/**
* 【选填】
* 超级管理员证件反面照片
* 1、当超级管理员类型是经办人时请上传超级管理员证件的反面照片
* 2、若证件类型为护照无需上传反面照片
* 3、正面拍摄、清晰、四角完整、无反光或遮挡不得翻拍、截图、镜像、PS
* 4、请上传彩色照片或彩色扫描件复印件需加盖公章鲜章可添加“微信支付”相关水印如微信支付认证见【指引文档】
* 5、可上传1张图片请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "contact_id_doc_copy_back")
private String contactIdDocCopyBack;
/**
* 【选填】
* 超级管理员证件有效期开始时间
* 1、当超级管理员类型是经办人时请上传证件有效期开始时间
* 2、请按照示例值填写日期格式应满足合法的YYYY-MM-DD格式
* 3、开始时间不能小于1900-01-01开始时间不能大于等于当前日期。
*/
@JSONField(name = "contact_period_begin")
private String contactPeriodBegin;
/**
* 【选填】
* 超级管理员证件有效期截止时间
* 1、当超级管理员类型是经办人时请上传证件有效期结束时间
* 2、请按照示例值填写日期格式应满足合法的YYYY-MM-DD格式若证件有效期为长期请填写长期
* 3、结束时间大于开始时间。
*/
@JSONField(name = "contact_period_end")
private String contactPeriodEnd;
/**
* 【必填】
* 联系手机
* 1、前后不能有空格、制表符、换行符
* 2、需满足以下任一条件
* 11位数字的手机号码
* 5-20位数字、连字符“-”、加号“+”;
* 3、用于接收微信支付的重要管理信息及日常操作验证码
* 4、该字段需要使用微信支付公钥加密推荐请参考获取微信支付公钥ID说明以及微信支付公钥加密敏感信息指引也可以使用微信支付平台证书公钥加密参考获取平台证书序列号、平台证书加密敏感信息指引。
*/
@JSONField(name = "mobile_phone")
private String mobilePhone;
/**
* 【必填】
* 联系邮箱
* 1、用于接收微信支付的开户邮件及日常业务通知
* 2、需要带@,遵循邮箱格式校验 该字段需要使用微信支付公钥加密推荐请参考获取微信支付公钥ID说明以及微信支付公钥加密敏感信息指引也可以使用微信支付平台证书公钥加密参考获取平台证书序列号、平台证书加密敏感信息指引。
*/
@JSONField(name = "contact_email")
private String contactEmail;
}

View File

@@ -0,0 +1,69 @@
package com.czg.third.wechat.dto.req.entry;
import com.alibaba.fastjson2.annotation.JSONField;
import com.czg.third.wechat.dto.req.entry.business.WechatEntryBusinessReqDto;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 微信进件请求参数
* 参考地址 <a href="https://pay.weixin.qq.com/doc/v3/partner/4012719997">...</a>
* @author yjjie
* @date 2025/12/26 13:36
*/
@Data
@Accessors(chain = true)
public class WechatEntryReqDto {
/**
* 【必填】
* 业务申请编号
*/
@JSONField(name = "business_code")
private String businessCode;
/**
* 【必填】
* 超级管理员信息
*/
@JSONField(name = "contact_info")
private WechatEntryContactReqDto contactInfo;
/**
* 【必填】
* 主体资料
*/
@JSONField(name = "subject_info")
private WechatEntrySubjectReqDto subjectInfo;
/**
* 【必填】
* 经营资料
* 商家的经营业务信息、售卖商品/提供服务场景信息
*/
@JSONField(name = "business_info")
private WechatEntryBusinessReqDto businessInfo;
/**
* 【必填】
* 结算规则
* 请填写商家的结算费率规则、特殊资质等信息
*/
@JSONField(name = "settlement_info")
private WechatEntrySettleReqDto settlementInfo;
/**
* 【必填】
* 结算银行账户
*/
@JSONField(name = "bank_account_info")
private WechatEntryBankAccountReqDto bankAccountInfo;
/**
* 【选填】
* 补充材料
* 根据实际审核情况,会额外要求商家提供指定的补充资料
*/
@JSONField(name = "addition_info")
private WechatEntryAdditionReqDto additionInfo;
}

View File

@@ -0,0 +1,91 @@
package com.czg.third.wechat.dto.req.entry;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 进件 结算信息
* @author yjjie
* @date 2025/12/26 13:38
*/
@Data
@Accessors(chain = true)
public class WechatEntrySettleReqDto {
/**
* 【必填】
* 入驻结算规则ID
* 请选择结算规则ID详细参见费率结算规则对照表
* <a href="https://kf.qq.com/faq/220228IJb2UV220228uEjU3Q.html">...</a>
*/
@JSONField(name = "settlement_id")
private String settlementId;
/**
* 【必填】
* 所属行业
* <a href="https://kf.qq.com/faq/220228IJb2UV220228uEjU3Q.html">...</a>
*/
@JSONField(name = "qualification_type")
private String qualificationType;
/**
* 【选填】
* 特殊资质图片
* 1、仅当商户选择了必需提交特殊资质的行业时需要提供该项资料若商户选择了无需特殊资质的行业或未选择行业时无需提交该项资料详情查看《费率结算规则对照表》
* 2、最多可上传5张照片请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "qualifications")
private List<String> qualifications;
/**
* 【选填】
* 优惠费率活动ID
* 如果商户有意向报名优惠费率活动,该字段【必填】。详细参见《优惠费率活动对照表》
* <a href="https://pay.weixin.qq.com/doc/v3/partner/4012082816">...</a>
*/
@JSONField(name = "activities_id")
private String activitiesId;
/**
* 【选填】
* 优惠费率活动值
* 根据优惠费率活动规则由合作伙伴自定义填写支持两个小数点需在优惠费率活动ID指定费率范围内
* 12023年7月17日-9月17日各合作伙伴平台可选择只传入“活动费率值”或分别传入“信用卡优惠活动费率值” 与 “非信用卡优惠活动费率值”,只传入“活动费率值”的情况下,平台将协助将申请单中的 “优惠活动费率值” 回填入新增的 “信用卡优惠活动费率值” 与 “非信用卡优惠活动费率值”中
* 22023年9月18日起平台将不再提供如上兼容能力届时仅能分别传入“信用卡优惠活动费率值” 与 “非信用卡优惠活动费率值”,否则接口将会报错。为避免影响正常进件,请在兼容期间完成相关调整。
*/
@JSONField(name = "activities_rate")
private String activitiesRate;
/**
* 【选填】
* 优惠费率活动补充材料
* 1、根据所选优惠费率活动提供相关材料详细参见《优惠费率活动对照表》<a href="https://pay.weixin.qq.com/doc/v3/partner/4012082816">...</a>
* 2、最多可上传5张照片请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "activities_additions")
private List<String> activitiesAdditions;
/**
* 【选填】
* 非信用卡活动费率值
* 用户支付方式为借记卡时收取的手续费费率:
* 1、若填写了优惠费率活动ID则该字段【必填】
* 2、仅能填入2位以内小数且在优惠费率活动ID指定费率范围内。
*/
@JSONField(name = "debit_activities_rate")
private String debitActivitiesRate;
/**
* 【选填】
* 信用卡活动费率值
* 用户支付方式为信用卡时收取的手续费费率:
* 1、若填写了优惠费率活动ID则该字段【必填】
* 2、仅能填入2位以内小数且在优惠费率活动ID指定费率范围内。
*/
@JSONField(name = "credit_activities_rate")
private String creditActivitiesRate;
}

View File

@@ -0,0 +1,103 @@
package com.czg.third.wechat.dto.req.entry;
import com.alibaba.fastjson2.annotation.JSONField;
import com.czg.third.wechat.dto.req.entry.business.*;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 进件 主体资料
* @author yjjie
* @date 2025/12/26 13:40
*/
@Data
@Accessors(chain = true)
public class WechatEntrySubjectReqDto {
/**
* 【必填】
* 主体类型 主体类型需与营业执照/登记证书上一致,可参考选择主体指引。<a href="https://kf.qq.com/faq/180910IBZVnQ180910naQ77b.html">...</a>
* 可选取值
* SUBJECT_TYPE_INDIVIDUAL: (个体户)营业执照上的主体类型一般为个体户、个体工商户、个体经营
* SUBJECT_TYPE_ENTERPRISE: (企业)营业执照上的主体类型一般为有限公司、有限责任公司
* SUBJECT_TYPE_GOVERNMENT: (政府机关)包括各级、各类政府机关,如机关党委、税务、民政、人社、工商、商务、市监等
* SUBJECT_TYPE_INSTITUTIONS: (事业单位)包括国内各类事业单位,如:医疗、教育、学校等单位
* SUBJECT_TYPE_OTHERS:(社会组织) 包括社会团体、民办非企业、基金会、基层群众性自治组织、农村集体经济组织等组织
*/
@JSONField(name = "subject_type")
private String subjectType;
/**
* 【选填】
* 是否是金融机构
* 【是否是金融机构】 【选填】,请根据申请主体的实际情况填写,可参考选择金融机构指引:
* 1、若商户主体是金融机构则填写true
* 2、若商户主体不是金融机构则填写false。
* 若未传入将默认填写false。
*/
@JSONField(name = "finance_institution")
private Boolean financeInstitution;
/**
* 【必填】
* 营业执照 当前不允许小微注册,所以【必填】
* 1、主体为个体户/企业,【必填】;
* 2、请上传“营业执照”需年检章齐全当年注册除外
*/
@JSONField(name = "business_license_info")
private WechatEntryLicenseReqDto businessLicenseInfo;
/**
* 【选填】
* 登记证书 主体为政府机关/事业单位/其他组织时,【必填】。
*/
@JSONField(name = "certificate_info")
private WechatEntryCertificateReqDto certificateInfo;
/**
* 【选填】
* 单位证明函照片
* 1、主体类型为政府机关、事业单位选传
* 1若上传则审核通过后即可签约无需汇款验证
* 2若未上传则审核通过后需汇款验证。
* 2、主体为个体户、企业、其他组织等不需要上传本字段
* 3、请参照示例图打印单位证明函全部信息需打印不支持手写商户信息并加盖公章 <a href="https://kf.qq.com/faq/200114u2y2yQ200114uEz26z.html">...</a>
* 4、可上传1张图片请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "certificate_letter_copy")
private String certificateLetterCopy;
/**
* 【选填】
* 金融机构许可证信息 当主体是金融机构时,【必填】。
*/
@JSONField(name = "finance_institution_info")
private WechatEntryFinanceInstitutionReqDto financeInstitutionInfo;
/**
* 【必填】
* 经营者/法定代表人身份证件
* 1、个体户请上传经营者的身份证件
* 2、企业/社会组织:请上传法定代表人的身份证件;
* 3、政府机关/事业单位:请上传法定代表人/经办人的身份证件。
*/
@JSONField(name = "identity_info")
private WechatEntryIdentityReqDto identityInfo;
/**
* 【选填】
* 最终受益人信息列表(UBO)
* 1、主体类型个体户/社会组织/政府机关/事业单位时,无需填写
* 2、主体类型为企业时按照下述要求填写
* 1若经营者/法定代表人不是最终受益所有人则需提填写受益所有人信息最多上传4个。
* 2若经营者/法定代表人是最终受益所有人之一可在此填写其他受益所有人信息最多上传3个。
* 根据国家相关法律法规,需要提供公司受益所有人信息,受益所有人需符合至少以下条件之一:
* ▪直接或者间接拥有超过25%公司股权或者表决权的自然人。
* ▪️通过人事、财务等其他方式对公司进行控制的自然人。
* ▪️公司的高级管理人员,包括公司的经理、副经理、财务负责人、上市公司董事会秘书和公司章程规定的其他人员。
*/
@JSONField(name = "ubo_info_list")
private List<WechatEntryUboInfoReqDto> uboInfoList;
}

View File

@@ -0,0 +1,50 @@
package com.czg.third.wechat.dto.req.entry.business;
import com.alibaba.fastjson2.annotation.JSONField;
import com.czg.third.wechat.dto.req.entry.business.sales.WechatEntrySalesInfoReqDto;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 微信进件 商户信息 经营资料
*
* @author yjjie
* @date 2025/12/26 13:41
*/
@Data
@Accessors(chain = true)
public class WechatEntryBusinessReqDto {
/**
* 【必填】
* 商户简称 在支付完成页向买家展示,需与微信经营类目相关
* 1、请输入1-64个字符
* 2、前后不能有空格、制表符、换行符
* 3、不能仅含特殊字符
* 4、仅能填写数字、英文字母、汉字及特殊字符
* 5、仅支持utf-8格式
* 6、简称要求
* 1不支持单纯以人名来命名若为个体户经营可用“个体户+经营者名称”或“经营者名称+业务”命名,如“个体户张三”或“张三餐饮店”;
* 2不支持无实际意义的文案如“XX特约商户”、“800”、“XX客服电话XXX”。
*/
@JSONField(name = "merchant_shortname")
private String merchantShortname;
/**
* 【必填】
* 客服电话 将在交易记录中向买家展示,请确保电话畅通以便平台回拨确认
* 1、前后不能有空格、制表符、换行符
* 2、需满足以下任一条件
* 11位数字的手机号码
* 5-20位数字、连字符“-”、加号“+”。
*/
@JSONField(name = "service_phone")
private String servicePhone;
/**
* 【必填】
* 经营场景
*/
@JSONField(name = "sales_info")
private WechatEntrySalesInfoReqDto salesInfo;
}

View File

@@ -0,0 +1,111 @@
package com.czg.third.wechat.dto.req.entry.business;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 登记证书
* @author yjjie
* @date 2025/12/26 15:07
*/
@Data
@Accessors(chain = true)
public class WechatEntryCertificateReqDto {
/**
* 【必填】
* 登记证书照片
* 1、照片应正面拍摄、清晰、四角完整、无反光或遮挡不得翻拍、截图、镜像、PS
* 2、上传彩色照片、彩色扫描件复印件需加盖公章鲜章
* 3、水印仅限于微信支付业务相关
* 4、指引与示例可参考【指引文档】
* 5、请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "cert_copy")
private String certCopy;
/**
* 【必填】
* 登记证书类型
* 1、主体为“政府机关/事业单位/社会组织”时,请上传登记证书类型;
* 2、主体为“个体工商户/企业”时,不填;
* 当主体为事业单位时,选择此枚举值:
* CERTIFICATE_TYPE_2388事业单位法人证书
* 当主体为政府机关,选择此枚举值:
* CERTIFICATE_TYPE_2389统一社会信用代码证书
* 当主体为社会组织,选择以下枚举值之一:
* CERTIFICATE_TYPE_2389统一社会信用代码证书
* CERTIFICATE_TYPE_2394社会团体法人登记证书
* CERTIFICATE_TYPE_2395民办非企业单位登记证书
* CERTIFICATE_TYPE_2396基金会法人登记证书
* CERTIFICATE_TYPE_2520执业许可证/执业证
* CERTIFICATE_TYPE_2521基层群众性自治组织特别法人统一社会信用代码证
* CERTIFICATE_TYPE_2522农村集体经济组织登记证
* CERTIFICATE_TYPE_2399宗教活动场所登记证
* CERTIFICATE_TYPE_2400政府部门下发的其他有效证明文件
*/
@JSONField(name = "cert_type")
private String certType;
/**
* 【必填】
* 证书号 请输入与所选证书类型相匹配且符合国家标准规范的证书号其中除政府证明文件外需满足18位阿拉伯数字或大写英文字母不得包含英文字母I/O/Z/S/V
*/
@JSONField(name = "cert_number")
private String certNumber;
/**
* 【必填】
* 商户名称 请填写登记证书上的商户名称
* 1、长度为2-128个字符
* 2、前后不能有空格、制表符、换行符
* 3、不能仅含数字、特殊字符
* 4、仅能填写数字、英文字母、汉字及特殊字符
* 5、仅支持utf-8格式。
*/
@JSONField(name = "merchant_name")
private String merchantName;
/**
* 【必填】
* 注册地址 请填写登记证书的注册地址
* 1、长度为4-128个字符
* 2、前后不能有空格、制表符、换行符
* 3、不能仅含数字、特殊字符
* 4、仅能填写数字、英文字母、汉字及特殊字符
* 5、仅支持utf-8格式。
*/
@JSONField(name = "company_address")
private String companyAddress;
/**
* 【必填】
* 法定代表人 请填写登记证书上的法定代表人姓名
* 1、长度为2-100个字符
* 2、前后不能有空格、制表符、换行符
* 3、不能仅含特殊字符
* 4、仅能填写数字、英文字母、汉字及特殊字符。
*/
@JSONField(name = "legal_person")
private String legalPerson;
/**
* 【选填】
* 有效期限开始日期 建议填写营业执照的有效期限开始时间,若该字段未填写,系统将会查询国家工商信息填入。需注意若工商信息查询不到,则会被审核驳回。
* 1、日期格式应满足合法的YYYY-MM-DD格式
* 2、开始时间不能小于1900-01-01
* 3、开始时间不能大于等于当前日期。
*/
@JSONField(name = "period_begin")
private String periodBegin;
/**
* 【选填】
* 营业期限结束日期 建议填写营业执照的有效期限结束时间,若该字段未填写,系统将会查询国家工商信息填入。需注意若工商信息查询不到,则会被审核驳回。
* 1、日期格式应满足合法的YYYY-MM-DD格式或长期
* 2、结束时间需大于开始时间。
*/
@JSONField(name = "period_end")
private String periodEnd;
}

View File

@@ -0,0 +1,42 @@
package com.czg.third.wechat.dto.req.entry.business;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 金融机构许可证信息
* @author yjjie
* @date 2025/12/26 15:21
*/
@Data
@Accessors(chain = true)
public class WechatEntryFinanceInstitutionReqDto {
/**
* 【必填】
* 金融机构类型 金融机构类型需与营业执照/登记证书上一致,可参考选择金融机构指引。
* BANK_AGENT银行业, 适用于商业银行、政策性银行、农村合作银行、村镇银行、开发性金融机构等
* PAYMENT_AGENT支付机构, 适用于非银行类支付机构
* INSURANCE保险业, 适用于保险、保险中介、保险代理、保险经纪等保险类业务
* TRADE_AND_SETTLE交易及结算类金融机构, 适用于交易所、登记结算类机构、银行卡清算机构、资金清算中心等
* OTHER其他金融机构, 适用于财务公司、信托公司、金融资产管理公司、金融租赁公司、汽车金融公司、贷款公司、货币经纪公司、消费金融公司、证券业、金融控股公司、股票、期货、货币兑换、小额贷款公司、金融资产管理、担保公司、商业保理公司、典当行、融资租赁公司、财经咨询等其他金融业务
*/
@JSONField(name = "finance_type")
private String financeType;
/**
* 必填
* 金融机构许可证图片
* 1、照片应正面拍摄、清晰、四角完整、无反光或遮挡不得翻拍、截图、镜像、PS
* 2、上传彩色照片、彩色扫描件复印件需加盖公章鲜章
* 3、水印仅限于微信支付业务相关
* 4、根据所属金融机构类型的许可证要求提供详情查看金融机构指引 <a href="https://kf.qq.com/faq/220215IrMRZ3220215n6buiU.html">...</a>
* 5、请提供为“申请商家主体”所属的许可证可授权使用总公司/分公司的特殊资质;
* 6、最多可上传5张照片请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "finance_license_pics")
private List<String> financeLicensePics;
}

View File

@@ -0,0 +1,72 @@
package com.czg.third.wechat.dto.req.entry.business;
import com.alibaba.fastjson2.annotation.JSONField;
import com.czg.third.wechat.dto.req.entry.id.WechatEntryIdCardReqDto;
import com.czg.third.wechat.dto.req.entry.id.WechatEntryIdDocInfoReqDto;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 经营者/法定代表人身份证件
* @author yjjie
* @date 2025/12/26 15:24
*/
@Data
@Accessors(chain = true)
public class WechatEntryIdentityReqDto {
/**
* 【选填】
* 证件持有人类型
* 1、主体类型为政府机关/事业单位时选传:
* 1若上传的是法定代表人证件则不需要上传该字段。
* 2若因政策保密等原因无法提供法定代表人证件时可上传经办人。 (经办人:经商户授权办理微信支付业务的人员,授权范围包括但不限于签约,入驻过程需完成账户验证)。
* 2、主体类型为小微/个人卖家/企业/个体户/社会组织时,默认为经营者/法定代表人,不需要上传该字段。
* LEGAL: 经营者/法定代表人
* SUPER: 经办人
*/
@JSONField(name = "id_holder_type")
private String idHolderType;
/**
* 【选填】
* 证件类型
* 1、当证件持有人类型为法定代表人时填写。其他情况无需上传
* 2、个体户/企业/事业单位/社会组织:可选择任一证件类型,政府机关仅支持中国大陆居民-身份证类型。
* 可选取值
* IDENTIFICATION_TYPE_IDCARD: 中国大陆居民-身份证
* IDENTIFICATION_TYPE_OVERSEA_PASSPORT: 其他国家或地区居民-护照
* IDENTIFICATION_TYPE_HONGKONG_PASSPORT: 中国香港居民-来往内地通行证
* IDENTIFICATION_TYPE_MACAO_PASSPORT: 中国澳门居民-来往内地通行证
* IDENTIFICATION_TYPE_TAIWAN_PASSPORT: 中国台湾居民-来往大陆通行证
* IDENTIFICATION_TYPE_FOREIGN_RESIDENT: 外国人居留证
* IDENTIFICATION_TYPE_HONGKONG_MACAO_RESIDENT: 港澳居民居住证
* IDENTIFICATION_TYPE_TAIWAN_RESIDENT: 台湾居民居住证
*/
@JSONField(name = "id_doc_type")
private String idDocType;
/**
* 【选填】
* 法定代表人说明函
* 1、当证件持有人类型为经办人时必须上传。其他情况无需上传
* 2、若因特殊情况无法提供法定代表人证件时请参照示例图打印法定代表人说明函全部信息需打印不支持手写商户信息并加盖公章 <a href="https://kf.qq.com/faq/220127aUzAju220127UfiuQr.html">...</a>
* 3、可上传1张图片请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "authorize_letter_copy")
private String authorizeLetterCopy;
/**
* 【选填】
* 身份证信息 当证件持有人类型为经营者/法定代表人且证件类型为“身份证”时填写。
*/
@JSONField(name = "id_card_info")
private WechatEntryIdCardReqDto idCardInfo;
/**
* 【选填】
* 其他类型证件信息 当证件持有人类型为经营者/法定代表人且证件类型不为“身份证”时填写。
*/
@JSONField(name = "id_doc_info")
private WechatEntryIdDocInfoReqDto idDocInfo;
}

View File

@@ -0,0 +1,90 @@
package com.czg.third.wechat.dto.req.entry.business;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 营业执照信息
* @author yjjie
* @date 2025/12/26 14:59
*/
@Data
@Accessors(chain = true)
public class WechatEntryLicenseReqDto {
/**
* 【必填】
* 营业执照照片
* 1、照片应正面拍摄、清晰、四角完整、无反光或遮挡不得翻拍、截图、镜像、PS
* 2、上传彩色照片、彩色扫描件复印件需加盖公章鲜章
* 3、水印仅限于微信支付业务相关
* 4、指引与示例可参考【指引文档】
* 5、可上传1张图片请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "license_copy")
private String licenseCopy;
/**
* 【必填】
* 注册号/统一社会信用代码
*/
@JSONField(name = "license_number")
private String licenseNumber;
/**
* 【必填】
* 商户名称
* 1、长度为2-128个字符
* 2、前后不能有空格、制表符、换行符
* 3、不能仅含数字、特殊字符
* 4、仅能填写数字、英文字母、汉字及特殊字符
* 5、仅支持utf-8格式
* 6、个体户证件为以下情况时按照个体户XXX命名XXX是营业执照经营人姓名营业执照登记名称为空、仅含数字、仅含特殊字符、“无”、“无字号”
* 7、个体户不能使用“企业”“公司”或“农民专业合作社”结尾。
*/
@JSONField(name = "merchant_name")
private String merchantName;
/**
* 【必填】
* 个体户经营者/法定代表人姓名 请填写营业执照的经营者/法定代表人姓名
* 1、长度为2-100个字符
* 2、前后不能有空格、制表符、换行符
* 3、不能仅含特殊字符
* 4、仅能填写数字、英文字母、汉字及特殊字符。
*/
@JSONField(name = "legal_person")
private String legalPerson;
/**
* 【选填】
* 注册地址 建议填写营业执照的注册地址,若该字段未填写,系统将会查询国家工商信息填入。需注意若工商信息查询不到,则会被审核驳回。
* 1、长度为4-128个字符
* 2、前后不能有空格、制表符、换行符
* 3、不能仅含数字、特殊字符
* 4、仅能填写数字、英文字母、汉字及特殊字符
* 5、仅支持utf-8格式。
*/
@JSONField(name = "license_address")
private String licenseAddress;
/**
* 【选填】
* 有效期限开始日期 建议填写营业执照的有效期限开始时间,若该字段未填写,系统将会查询国家工商信息填入。需注意若工商信息查询不到,则会被审核驳回。
* 1、日期格式应满足合法的YYYY-MM-DD格式
* 2、开始时间不能小于1900-01-01
* 3、开始时间不能大于等于当前日期。
*/
@JSONField(name = "period_begin")
private String periodBegin;
/**
* 【选填】
* 营业期限结束日期 建议填写营业执照的有效期限结束时间,若该字段未填写,系统将会查询国家工商信息填入。需注意若工商信息查询不到,则会被审核驳回。
* 1、日期格式应满足合法的YYYY-MM-DD格式或长期
* 2、结束时间需大于开始时间。
*/
@JSONField(name = "period_end")
private String periodEnd;
}

View File

@@ -0,0 +1,113 @@
package com.czg.third.wechat.dto.req.entry.business;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 最终受益人信息
* @author yjjie
* @date 2025/12/26 15:50
*/
@Data
@Accessors(chain = true)
public class WechatEntryUboInfoReqDto {
/**
* 【必填】
* 证件类型 请填写受益人的证件类型。枚举值:
* 可选取值
* IDENTIFICATION_TYPE_IDCARD: 中国大陆居民-身份证
* IDENTIFICATION_TYPE_OVERSEA_PASSPORT: 其他国家或地区居民-护照
* IDENTIFICATION_TYPE_HONGKONG_PASSPORT: 中国香港居民-来往内地通行证
* IDENTIFICATION_TYPE_MACAO_PASSPORT: 中国澳门居民-来往内地通行证
* IDENTIFICATION_TYPE_TAIWAN_PASSPORT: 中国台湾居民-来往大陆通行证
* IDENTIFICATION_TYPE_FOREIGN_RESIDENT: 外国人居留证
* IDENTIFICATION_TYPE_HONGKONG_MACAO_RESIDENT: 港澳居民居住证
* IDENTIFICATION_TYPE_TAIWAN_RESIDENT: 台湾居民居住证
*/
@JSONField(name = "ubo_id_doc_type")
private String uboIdDocType;
/**
* 必填
* 证件正面照片
* 1、请上传受益人证件的正面照片
* 2、若证件类型为身份证请上传人像面照片
* 3、正面拍摄、清晰、四角完整、无反光或遮挡不得翻拍、截图、镜像、PS
* 4、请上传彩色照片or彩色扫描件复印件需加盖公章鲜章可添加“微信支付”相关水印如微信支付认证见【指引文档】
* 5、可上传1张图片请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "ubo_id_doc_copy")
private String uboIdDocCopy;
/**
* 【选填】
* 证件反面照片
* 1、请上传受益人证件的反面照片
* 2、若证件类型为身份证请上传国徽面照片
* 3、若证件类型为护照无需上传反面照片
* 4、正面拍摄、清晰、四角完整、无反光或遮挡不得翻拍、截图、镜像、PS
* 5、请上传彩色照片or彩色扫描件复印件需加盖公章鲜章可添加“微信支付”相关水印如微信支付认证见【指引文档】
* 6、可上传1张图片请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "ubo_id_doc_copy_back")
private String uboIdDocCopyBack;
/**
* 必填
* 证件姓名
* 1、长度为2-100个字符
* 2、前后不能有空格、制表符、换行符
* 3、不能仅含数字、特殊字符
* 4、仅能填写数字、英文字母、汉字及特殊字符
* 5、该字段需要使用微信支付公钥加密推荐请参考获取微信支付公钥ID说明以及微信支付公钥加密敏感信息指引也可以使用微信支付平台证书公钥加密参考获取平台证书序列号、平台证书加密敏感信息指引。
*/
@JSONField(name = "ubo_id_doc_name")
private String uboIdDocName;
/**
* 必填
* 证件号码
* 1、证件号码为证件正反面中一致的号码
* 2、证件号码为18位或15位或港澳居民来往内地通行证为9位
* 3、证件号码为英文字母X或x或港澳居民来往内地通行证为英文字母X或x
* 4、证件号码为英文字母x或X或港澳居民来往内地通行证为英文字母x或X
* 5、该字段需要使用微信支付公钥加密推荐请参考获取微信支付公钥
*/
@JSONField(name = "ubo_id_doc_number")
private String uboIdDocNumber;
/**
* 必填
* 证件居住地址
* 1、请按照身份证住址填写如广东省深圳市南山区xx路xx号xx室
* 2、长度为4-128个字符
* 3、前后不能有空格、制表符、换行符
* 4、不能仅含数字、特殊字符
* 5、仅能填写数字、英文字母、汉字及特殊字符
* 6、仅支持utf-8格式
* 7、 该字段需要使用微信支付公钥加密推荐请参考获取微信支付公钥ID说明以及微信支付公钥加密敏感信息指引也可以使用微信支付平台证书公钥加密参考获取平台证书序列号、平台证书加密敏感信息指引。
*/
@JSONField(name = "ubo_id_doc_address")
private String uboIdDocAddress;
/**
* 必填
* 证件有效期开始时间
* 1、日期格式应满足合法的YYYY-MM-DD格式
* 2、开始时间不能小于1900-01-01
* 3、开始时间不能大于等于当前日期。
*/
@JSONField(name = "ubo_period_begin")
private String uboPeriodBegin;
/**
* 必填
* 证件有效期结束时间
* 1、日期格式应满足合法的YYYY-MM-DD格式或长期
* 2、结束时间需大于开始时间。
*/
@JSONField(name = "ubo_period_end")
private String uboPeriodEnd;
}

View File

@@ -0,0 +1,45 @@
package com.czg.third.wechat.dto.req.entry.business.sales;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 进件-经营资料-经营场景-App场景
* @author yjjie
* @date 2025/12/26 14:26
*/
@Data
@Accessors(chain = true)
public class WechatEntryAppInfoReqDto {
/**
* 【必填】
* App截图
* 1、请提供APP首页截图、尾页截图、应用内截图、支付页截图各1张
* 2、请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "app_pics")
private List<String> appPics;
/**
* 【选填】
* 商家应用AppID
* 1、服务商应用AppID与商家应用AppID二选一【必填】
* 2、可填写与商家主体一致且已认证的应用AppID需是已认证的App
* 3、审核通过后系统将发起特约商家商户号与该AppID的绑定即配置为sub_appid服务商随后可在发起支付时选择传入该AppID以完成支付并获取sub_openid用于数据统计营销等业务场景。
*/
@JSONField(name = "app_sub_appid")
private String appSubAppid;
/**
* 【选填】
* 服务商应用AppID
* 1、服务商应用AppID与商家应用AppID二选一【必填】
* 2、可填写当前服务商商户号已绑定的应用AppID。
*/
@JSONField(name = "app_appid")
private String appAppid;
}

View File

@@ -0,0 +1,47 @@
package com.czg.third.wechat.dto.req.entry.business.sales;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 进件-经营资料-经营场景-小程序场景
* @author yjjie
* @date 2025/12/26 14:23
*/
@Data
@Accessors(chain = true)
public class WechatEntryMiniProgramReqDto {
/**
* 【选填】
* 服务商小程序AppID
* 1、服务商小程序AppID与商家小程序AppID二选一必填
* 2、可填写当前服务商商户号已绑定的小程序AppID。
*/
@JSONField(name = "mini_program_appid")
private String miniProgramAppid;
/**
* 【选填】
* 商家小程序AppID
* 1、服务商小程序AppID与商家小程序AppID二选一必填
* 2、请填写已认证的小程序AppID
* 3、完成进件后系统发起特约商户号与该AppID的绑定即配置为sub_appid可在发起支付时传入
* 1若AppID主体与商家主体/服务商主体一致,则直接完成绑定;
* 2若AppID主体与商家主体/服务商主体不一致则商户签约时显示《联合营运承诺函》并且AppID的管理员需登录公众平台确认绑定意愿。
*/
@JSONField(name = "mini_program_sub_appid")
private String miniProgramSubAppid;
/**
* 【选填】
* 小程序截图
* 1、请提供展示商品/服务的页面截图/设计稿最多5张若小程序未建设完善或未上线 请务必提供;
* 2、请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "mini_program_pics")
private List<String> miniProgramPics;
}

View File

@@ -0,0 +1,45 @@
package com.czg.third.wechat.dto.req.entry.business.sales;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 进件-经营资料-经营场景-服务号或公众号景
* @author yjjie
* @date 2025/12/26 14:20
*/
@Data
@Accessors(chain = true)
public class WechatEntryMpInfoReqDto {
/**
* 【必填】
* 服务号或公众号页面截图
* 1、请提供展示商品/服务的页面截图/设计稿最多5张若服务号或公众号未建设完善或未上线请务必提供
* 2、请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "mp_pics")
private List<String> mpPics;
/**
* 【选填】
* 商家服务号或公众号AppID
* 1、服务商服务号或公众号AppID、商家服务号或公众号AppID二选一【必填】
* 2、可填写与商家主体一致且已认证的服务号或公众号AppID需是已认证的服务号、政府或媒体类型的公众号
* 3、审核通过后系统将发起特约商家商户号与该AppID的绑定即配置为sub_appid服务商随后可在发起支付时选择传入该appid以完成支付并获取sub_openid用于数据统计营销等业务场景。
*/
@JSONField(name = "mp_sub_appid")
private String mpSubAppid;
/**
* 【选填】
* 服务商服务号或公众号AppID
* 1、服务商服务号或公众号AppID、商家服务号或公众号AppID二选一【必填】
* 2、可填写当前服务商商户号已绑定的服务号或公众号AppID。
*/
@JSONField(name = "mp_appid")
private String mpAppid;
}

View File

@@ -0,0 +1,87 @@
package com.czg.third.wechat.dto.req.entry.business.sales;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 进件-经营场景
* @author yjjie
* @date 2025/12/26 14:07
*/
@Data
@Accessors(chain = true)
public class WechatEntrySalesInfoReqDto {
/**
* 【必填】
* 经营场景类型
* 1、请勾选实际售卖商品/提供服务场景(至少一项),以便为你开通需要的支付权限;
* 2、建议只勾选目前必须的场景以便尽快通过入驻审核其他支付权限可在入驻后再根据实际需要发起申请
* 可选取值
* SALES_SCENES_STORE: 线下场所
* SALES_SCENES_MP: 服务号与公众号
* SALES_SCENES_MINI_PROGRAM: 小程序
* SALES_SCENES_WEB: 互联网网站
* SALES_SCENES_APP: App
* SALES_SCENES_WEWORK: 企业微信
*/
@JSONField(name = "sales_scenes_type")
private List<String> salesScenesType;
/**
* 【选填】
* 线下场所场景
* 1、审核通过后服务商可帮商户发起付款码支付、JSAPI支付
* 2、当"经营场景类型"选择"SALES_SCENES_STORE",该场景资料【必填】。
*/
@JSONField(name = "biz_store_info")
private WechatEntryStoreInfoReqDto bizStoreInfo;
/**
* 【选填】
* 服务号或公众号场景
* 1、审核通过后服务商可帮商家发起JSAPI支付
* 2、当"经营场景类型"选择"SALES_SCENES_MP",该场景资料【必填】。
*/
@JSONField(name = "mp_info")
private WechatEntryMpInfoReqDto mpInfo;
/**
* 【选填】
* 小程序场景
* 1、审核通过后服务商可帮商家发起JSAPI支付
* 2、当"经营场景类型"选择"SALES_SCENES_MINI_PROGRAM",该场景资料必填。
*/
@JSONField(name = "mini_program_info")
private WechatEntryMiniProgramReqDto miniProgramInfo;
/**
* 【选填】
* App场景
* 1、审核通过后服务商可帮商家发起App支付
* 2、当"经营场景类型"选择"SALES_SCENES_APP",该场景资料必填。
*/
@JSONField(name = "app_info")
private WechatEntryAppInfoReqDto appInfo;
/**
* 【选填】
* 互联网网站场景
* 1、审核通过后服务商可帮商家发起JSAPI支付
* 2、当"经营场景类型"选择"SALES_SCENES_WEB",该场景资料必填。
*/
@JSONField(name = "web_info")
private WechatEntryWebInfoReqDto webInfo;
/**
* 【选填】
* 企业微信场景
* 1、审核通过后服务商可帮商家发起JSAPI支付
* 2、当"经营场景类型"选择"SALES_SCENES_WEWORK",该场景资料必填。
*/
@JSONField(name = "wework_info")
private WechatEntryWeworkInfoReqDto weworkInfo;
}

View File

@@ -0,0 +1,78 @@
package com.czg.third.wechat.dto.req.entry.business.sales;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 进件-经营资料-经营场景-线下场所场景
* @author yjjie
* @date 2025/12/26 14:10
*/
@Data
@Accessors(chain = true)
public class WechatEntryStoreInfoReqDto {
/**
* 【必填】
* 线下场所名称 请填写门店名称
* 1、长度为1-50个字符
* 2、前后不能有空格、制表符、换行符
* 3、不能仅含数字、特殊字符
* 4、仅能填写数字、英文字母、汉字及特殊字符
* 5、仅支持utf-8格式。
*/
@JSONField(name = "biz_store_name")
private String bizStoreName;
/**
* 【必填】
* 线下场所省市编码
* 1、只能由数字组成
* 2、详细参见微信支付提供的省市对照表。 <a href="https://pay.weixin.qq.com/doc/v3/partner/4012082815">...</a>
*/
@JSONField(name = "biz_address_code")
private String bizAddressCode;
/**
* 【必填】
* 线下场所地址 请填写详细的经营场所信息,如有多个场所,选择一个主要场所填写即可。
* 1、长度为4-512个字符
* 2、前后不能有空格、制表符、换行符
* 3、不能仅含数字、特殊字符
* 4、仅能填写数字、英文字母、汉字及特殊字符
* 5、仅支持utf-8格式
*/
@JSONField(name = "biz_store_address")
private String bizStoreAddress;
/**
* 【必填】
* 线下场所门头照片
* 1、请上传门头正面照片要求门店招牌、门框完整、清晰、可辨识若为停车场等无固定门头照片的经营场所可上传岗亭/出入闸口。具体参考【指引文档】;
* 2、请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "store_entrance_pic")
private List<String> storeEntrancePic;
/**
* 【必填】
* 线下场所内部照片
* 1、请上传门店内部环境照片可辨识经营内容。若为停车场等无固定门头的经营场所可上传停车场内部照片。具体参考【指引文档】
* 2、请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "indoor_pic")
private List<String> indoorPic;
/**
* 【选填】
* 线下场所对应的商家AppID
* 1、可填写与商家主体一致且已认证的服务号或公众号、小程序、APP的AppID其中服务号或公众号AppID需是已认证的服务号、政府或媒体类型的公众号
* 2、审核通过后系统将额外为商家开通付款码支付、JSAPI支付的自有交易权限并完成商家商户号与该AppID的绑定。
*/
@JSONField(name = "biz_sub_appid")
private String bizSubAppid;
}

View File

@@ -0,0 +1,44 @@
package com.czg.third.wechat.dto.req.entry.business.sales;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 进件-经营资料-经营场景-互联网网站场景
* @author yjjie
* @date 2025/12/26 14:29
*/
@Data
@Accessors(chain = true)
public class WechatEntryWebInfoReqDto {
/**
* 【必填】
* 互联网网站域名
* 1、如为PC端商城、智能终端等场景可上传官网链接
* 2、网站域名需ICP备案若备案主体与申请主体不同请上传加盖公章的网站授权函。
*/
@JSONField(name = "domain")
private String domain;
/**
* 【必填】
* 网站授权函
* 1、若备案主体与申请主体不同请务必上传加盖公章的网站授权函.doc <a href="https://gtimg.wechatpay.cn/resource/xres/mmpaydoc/static/attachment/bb55b16e3825a5952a531d55fba0f382/%E7%BD%91%E7%AB%99%E6%8E%88%E6%9D%83%E5%87%BD.doc">...</a>
* 2、请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "web_authorisation")
private String webAuthorisation;
/**
* 【选填】
* 互联网网站对应的商家AppID
* 1、可填写已认证的服务号或公众号、小程序、APP的AppID其中服务号或公众号AppID需是已认证的服务号、政府或媒体类型的公众号
* 2、完成进件后系统发起特约商户号与该AppID的绑定即配置为sub_appid可在发起支付时传入
* 1若APPID主体与商家主体一致则直接完成绑定
* 2若APPID主体与商家主体不一致则商户签约时显示《联合营运承诺函》并且 AppID的管理员需登录公众平台确认绑定意愿 暂不支持绑定异主体的应用APPID
*/
@JSONField(name = "web_appid")
private String webAppid;
}

View File

@@ -0,0 +1,33 @@
package com.czg.third.wechat.dto.req.entry.business.sales;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 进件-经营资料-经营场景-企业微信场景
* @author yjjie
* @date 2025/12/26 14:32
*/
@Data
@Accessors(chain = true)
public class WechatEntryWeworkInfoReqDto {
/**
* 【必填】
* 商家企业微信CorpID
* 1、可填写与商家主体一致且已认证的企业微信CorpID
* 2、审核通过后系统将为商家开通企业微信专区的自有交易权限并完成商家商户号与该AppID的绑定商家可自行发起交易。
*/
@JSONField(name = "sub_corp_id")
private String subCorpId;
/**
* 必填
* 企业微信页面截图
* 1、最多可上传5张照片
* 2、请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "wework_pics")
private String[] weworkPics;
}

View File

@@ -0,0 +1,96 @@
package com.czg.third.wechat.dto.req.entry.id;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 身份证信息
* @author yjjie
* @date 2025/12/26 15:29
*/
@Data
@Accessors(chain = true)
public class WechatEntryIdCardReqDto {
/**
* 【必填】
* 身份证人像面照片
* 1、请上传个体户经营者/法定代表人的身份证人像面照片;
* 2、正面拍摄、清晰、四角完整、无反光或遮挡不得翻拍、截图、镜像、PS
* 3、请上传彩色照片or彩色扫描件复印件需加盖公章鲜章可添加“微信支付”相关水印如微信支付认证见【指引文档】
* 4、可上传1张图片请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "id_card_copy")
private String idCardCopy;
/**
* 【必填】
* 身份证国徽面照片
* 、请上传个体户经营者/法定代表人的身份证国徽面照片;
* 2、正面拍摄、清晰、四角完整、无反光或遮挡不得翻拍、截图、镜像、PS
* 3、请上传彩色照片or彩色扫描件复印件需加盖公章鲜章可添加“微信支付”相关水印如微信支付认证见【指引文档】
* 4、可上传1张图片请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "id_card_national")
private String idCardNational;
/**
* 【必填】
* 身份证姓名
* 1、请填写个体户经营者/法定代表人对应身份证的姓名;
* 2、长度为2-100个字符
* 3、前后不能有空格、制表符、换行符
* 4、不能仅含数字、特殊字符
* 5、仅能填写数字、英文字母、汉字及特殊字符
* 6、该字段需要使用微信支付公钥加密推荐请参考获取微信支付公钥ID说明以及微信支付公钥加密敏感信息指引也可以使用微信支付平台证书公钥加密参考获取平台证书序列号、平台证书加密敏感信息指引。
* <a href="https://pay.weixin.qq.com/doc/v3/partner/4013059044">加密</a>
*/
@JSONField(name = "id_card_name")
private String idCardName;
/**
* 必填
* 身份证号码
* 1、请填写个体户经营者/法定代表人对应身份证的号码;
* 2、17位数字+1位数字|X 该字段需要使用微信支付公钥加密推荐请参考获取微信支付公钥ID说明以及微信支付公钥加密敏感信息指引也可以使用微信支付平台证书公钥加密参考获取平台证书序列号、平台证书加密敏感信息指引。
* <a href="https://pay.weixin.qq.com/doc/v3/partner/4013059044">加密</a>
*/
@JSONField(name = "id_card_number")
private String idCardNumber;
/**
* 【选填】
* 身份证居住地址
* 1、主体类型为企业时需要填写。其他主体类型无需上传
* 2、请按照身份证住址填写如广东省深圳市南山区xx路xx号xx室
* 3、长度为4-128个字符
* 4、前后不能有空格、制表符、换行符
* 5、不能仅含数字、特殊字符
* 6、仅能填写数字、英文字母、汉字及特殊字符
* 7、仅支持utf-8格式
* 8、 该字段需要使用微信支付公钥加密推荐请参考获取微信支付公钥ID说明以及微信支付公钥加密敏感信息指引也可以使用微信支付平台证书公钥加密参考获取平台证书序列号、平台证书加密敏感信息指引。
* <a href="https://pay.weixin.qq.com/doc/v3/partner/4013059044">加密</a>
*/
@JSONField(name = "id_card_address")
private String idCardAddress;
/**
* 必填
* 身份证有效期开始时间
* 1、请填写身份证有效期开始时间格式为yyyy-MM-dd
* 2、开始时间不能小于1900-01-01
* 3、开始时间不能大于等于当前日期。
*/
@JSONField(name = "card_period_begin")
private String cardPeriodBegin;
/**
* 必填
* 身份证有效期截止时间
* 1、日期格式应满足合法的YYYY-MM-DD格式或长期
* 2、结束时间需大于开始时间。
*/
@JSONField(name = "card_period_end")
private String cardPeriodEnd;
}

View File

@@ -0,0 +1,100 @@
package com.czg.third.wechat.dto.req.entry.id;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 其他类型证件信息
* @author yjjie
* @date 2025/12/26 15:42
*/
@Data
@Accessors(chain = true)
public class WechatEntryIdDocInfoReqDto {
/**
* 【必填】
* 证件正面照片
* 1、证件类型不为“身份证”时上传证件正面照片
* 2、正面拍摄、清晰、四角完整、无反光或遮挡不得翻拍、截图、镜像、PS
* 3、请上传彩色照片or彩色扫描件复印件需加盖公章鲜章可添加“微信支付”相关水印如微信支付认证见【指引文档】
* 4、可上传1张图片请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "id_doc_copy")
private String idDocCopy;
/**
* 【选填】
* 证件反面照片
* 1、若证件类型为往来通行证、外国人居留证、港澳居民居住证、台湾居民居住证时上传证件反面照片
* 2、若证件类型为护照无需上传反面照片
* 3、正面拍摄、清晰、四角完整、无反光或遮挡不得翻拍、截图、镜像、PS
* 4、请上传彩色照片or彩色扫描件复印件需加盖公章鲜章可添加“微信支付”相关水印如微信支付认证见【指引文档】
* 5、可上传1张图片请填写通过图片上传API预先上传图片生成好的MediaID。
*/
@JSONField(name = "id_doc_copy_back")
private String idDocCopyBack;
/**
* 【必填】
* 证件姓名
* 1、请填写经营者/法定代表人的证件姓名;
* 2、长度为2-100个字符
* 3、前后不能有空格、制表符、换行符
* 4、不能仅含数字、特殊字符
* 5、仅能填写数字、英文字母、汉字及特殊字符
* 6、该字段需要使用微信支付公钥加密推荐请参考获取微信支付公钥ID说明以及微信支付公钥加密敏感信息指引也可以使用微信支付平台证书公钥加密参考获取平台证书序列号、平台证书加密敏感信息指引。
*/
@JSONField(name = "id_doc_name")
private String idDocName;
/**
* 【必填】
* 证件号码
* 1、请填写经营者/法定代表人的证件号码:
* 护照限境外人士4-15位 数字|字母|连字符;
* 中国香港居民--来往内地通行证H/h开头+8或10位数字/字母;
* 中国澳门居民--来往内地通行证M/m开头+8或10位数字/字母;
* 中国台湾居民--来往大陆通行证8位数字或10位数字
* 外国人居留证15位 数字|字母;
* 台湾居民居住证/港澳居民居住证17位数字+1位数字|X
* 2、该字段需要使用微信支付公钥加密推荐请参考获取微信支付公钥ID说明以及微信支付公钥加密敏感信息指引也可以使用微信支付平台证书公钥加密参考获取平台证书序列号、平台证书加密敏感信息指引。
*/
@JSONField(name = "id_doc_number")
private String idDocNumber;
/**
* 【选填】
* 证件居住地址
* 1、主体类型为企业时需要填写。其他主体类型无需上传
* 2、请按照身份证住址填写如广东省深圳市南山区xx路xx号xx室
* 3、长度为4-128个字符
* 4、前后不能有空格、制表符、换行符
* 5、不能仅含数字、特殊字符
* 6、仅能填写数字、英文字母、汉字及特殊字符
* 7、仅支持utf-8格式
* 8、 该字段需要使用微信支付公钥加密推荐请参考获取微信支付公钥ID说明以及微信支付公钥加密敏感信息指引也可以使用微信支付平台证书公钥加密参考获取平台证书序列号、平台证书加密敏感信息指引。
*/
@JSONField(name = "id_doc_address")
private String idDocAddress;
/**
* 必填
* 证件有效期开始时间
* 1、日期格式应满足合法的YYYY-MM-DD格式
* 2、开始时间不能小于1900-01-01
* 3、开始时间不能大于等于当前日期。
*/
@JSONField(name = "doc_period_begin")
private String docPeriodBegin;
/**
* 必填
* 证件有效期结束时间
* 1、日期格式应满足合法的YYYY-MM-DD格式或长期
* 2、结束时间需大于开始时间。
*/
@JSONField(name = "doc_period_end")
private String docPeriodEnd;
}

View File

@@ -0,0 +1,29 @@
package com.czg.third.wechat.dto.resp;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* @author yjjie
* @date 2026/1/7 15:46
*/
@Data
public class WechatAuditDetail {
/**
* 字段名
*/
@JSONField(name = "field")
public String field;
/**
* 字段名称
*/
@JSONField(name = "field_name")
public String fieldName;
/**
* 驳回原因
*/
@JSONField(name = "reject_reason")
public String rejectReason;
}

View File

@@ -0,0 +1,70 @@
package com.czg.third.wechat.dto.resp;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 微信进件状态查询返回参数
* @author yjjie
* @date 2026/1/7 15:42
*/
@Data
@Accessors(chain = true)
public class WechatQueryStateResp {
/**
* 业务申请编号
*/
@JSONField(name = "business_code")
public String businessCode;
/**
* 微信支付申请单号
*/
@JSONField(name = "applyment_id")
public String applyId;
/**
* 特约商户号
* 当申请单状态为APPLYMENT_STATE_TO_BE_SIGNED、APPLYMENT_STATE_SIGNING、APPLYMENT_STATE_FINISHED时才返回。
*/
@JSONField(name = "sub_mchid")
public String subMchId;
/**
* 超级管理员签约链接
* 1、超级管理员用微信扫码关注“微信支付商家助手”公众号后公众号将自动发送签约消息超管需点击消息根据指引完成核对联系信息、账户验证、签约等操作
* 2、超管完成核对联系信息后续申请单进度可通过公众号自动通知超级管理员。
*/
@JSONField(name = "sign_url")
public String signUrl;
/**
* 申请单状态
* APPLYMENT_STATE_EDITTING:(编辑中)提交申请发生错误导致,请尝试重新提交。
* APPLYMENT_STATE_AUDITING:(审核中)申请单正在审核中,超级管理员用微信打开“签约链接”,完成绑定微信号后,申请单进度将通过微信公众号通知超级管理员,引导完成后续步骤。
* APPLYMENT_STATE_REJECTED:(已驳回)请按照驳回原因修改申请资料,超级管理员用微信打开“签约链接”,完成绑定微信号,后续申请单进度将通过微信公众号通知超级管理员。
* APPLYMENT_STATE_TO_BE_CONFIRMED:(待账户验证)请超级管理员使用微信打开返回的“签约链接”,根据页面指引完成账户验证。
* APPLYMENT_STATE_TO_BE_SIGNED:(待签约)请超级管理员使用微信打开返回的“签约链接”,根据页面指引完成签约。
* APPLYMENT_STATE_SIGNING:(开通权限中)系统开通相关权限中,请耐心等待。
* APPLYMENT_STATE_FINISHED:(已完成)商户入驻申请已完成。
* APPLYMENT_STATE_CANCELED:(已作废)申请单已被撤销。
*/
@JSONField(name = "applyment_state")
public String applyState;
/**
* 申请状态描述
*/
@JSONField(name = "applyment_state_msg")
public String applyStateMsg;
/**
* 驳回原因详情
*/
@JSONField(name = "audit_detail")
public List<WechatAuditDetail> auditDetail;
}

View File

@@ -0,0 +1,157 @@
package com.czg.utils;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* Java 21 异步多任务执行工具类
* 功能:异步执行多个任务,等待所有任务完成后统一返回结果(包含成功/失败信息)
* 特性:基于虚拟线程、支持泛型、完善的异常处理、可自定义线程池
*
* @author yjjie
* @date 2026/1/6 18:21
*/
public class AsyncTaskExecutor {
// 默认线程池Java 21 虚拟线程池(轻量级、高并发)
private static final ExecutorService DEFAULT_EXECUTOR = Executors.newVirtualThreadPerTaskExecutor();
/**
* 执行多个异步任务,等待所有任务完成后统一返回结果
*
* @param tasks 任务列表(每个任务是一个 Supplier 函数式接口,封装具体业务逻辑)
* @param <T> 任务返回值类型
* @return 所有任务的执行结果(包含成功/失败信息)
*/
public static <T> List<TaskResult<T>> executeAll(List<Supplier<T>> tasks) {
return executeAll(tasks, DEFAULT_EXECUTOR);
}
/**
* 执行多个异步任务(自定义线程池),等待所有任务完成后统一返回结果
*
* @param tasks 任务列表
* @param executor 自定义线程池(如需要使用传统线程池可传入)
* @param <T> 任务返回值类型
* @return 所有任务的执行结果
*/
public static <T> List<TaskResult<T>> executeAll(List<Supplier<T>> tasks, ExecutorService executor) {
// 校验入参
if (tasks == null || tasks.isEmpty()) {
return List.of();
}
if (executor == null) {
throw new IllegalArgumentException("线程池不能为null");
}
// 1. 提交所有异步任务获取CompletableFuture列表
List<CompletableFuture<TaskResult<T>>> futureList = tasks.stream()
.map(task -> CompletableFuture.supplyAsync(
() -> executeSingleTask(task),
executor
))
.collect(Collectors.toList());
// 2. 等待所有任务完成(无超时)
CompletableFuture<Void> allFutures = CompletableFuture.allOf(
futureList.toArray(new CompletableFuture[0])
);
try {
// 阻塞等待所有任务完成(可根据业务需求添加超时,如 allFutures.get(10, TimeUnit.SECONDS)
allFutures.get();
} catch (Exception e) {
// 全局等待异常(如超时、中断),标记所有未完成的任务为失败
handleGlobalException(futureList, e);
}
// 3. 收集所有任务结果
return futureList.stream()
// 显式指定泛型类型让编译器明确知道map的返回类型是TaskResult<T>
.<TaskResult<T>>map(future -> {
try {
// 这里强制指定泛型,避免类型推断模糊
return future.get();
} catch (Exception e) {
// 理论上不会走到这里因为singleTask已捕获异常allOf已等待完成
return new TaskResult<>(null, false, "结果收集异常:" + e.getMessage());
}
})
.collect(Collectors.toList());
}
/**
* 执行单个任务,捕获任务执行过程中的异常
*/
private static <T> TaskResult<T> executeSingleTask(Supplier<T> task) {
try {
T result = task.get();
return new TaskResult<>(result, true, null);
} catch (Exception e) {
// 捕获单个任务的所有异常,封装为失败结果
return new TaskResult<>(null, false, "任务执行失败:" + e.getMessage());
}
}
/**
* 处理全局等待过程中的异常(如超时、中断),标记未完成任务为失败
*/
private static <T> void handleGlobalException(List<CompletableFuture<TaskResult<T>>> futureList, Exception e) {
String errorMsg = "全局等待异常:" + e.getMessage();
futureList.forEach(future -> {
if (!future.isDone()) {
// 取消未完成的任务,并标记为失败
future.complete(new TaskResult<>(null, false, errorMsg));
}
});
// 恢复线程中断状态(如果是中断异常)
if (e instanceof InterruptedException) {
Thread.currentThread().interrupt();
}
}
/**
* 任务结果封装类
* 包含:返回值、是否成功、失败信息
*
* @param <T> 结果类型
* @param result Getter 方法 任务返回值成功时非null
* @param success 是否执行成功
* @param errorMsg 失败信息失败时非null
*/
public record TaskResult<T>(T result, boolean success, String errorMsg) {
// 重写toString方便打印结果
@NotNull
@Override
public String toString() {
if (success) {
return "TaskResult{success=true, result=" + result + "}";
} else {
return "TaskResult{success=false, errorMsg='" + errorMsg + "'}";
}
}
}
/**
* 关闭默认线程池(可选,如应用退出时调用)
*/
public static void shutdownDefaultExecutor() {
DEFAULT_EXECUTOR.shutdown();
try {
if (!DEFAULT_EXECUTOR.awaitTermination(5, TimeUnit.SECONDS)) {
DEFAULT_EXECUTOR.shutdownNow();
}
} catch (InterruptedException e) {
DEFAULT_EXECUTOR.shutdownNow();
Thread.currentThread().interrupt();
}
}
}

View File

@@ -0,0 +1,87 @@
package com.czg.utils;
import lombok.extern.slf4j.Slf4j;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.regex.Pattern;
/**
* @author yjjie
* @date 2026/1/4 13:58
*/
@Slf4j
public class UploadFileUtil {
// 匹配常见的图片扩展名
private static final Pattern PATTERN = Pattern.compile("\\.(jpg|jpeg|png|gif|bmp|webp|svg|ico)(?:[\\?#]|$)", Pattern.CASE_INSENSITIVE);
/**
* 使用正则表达式提取图片后缀
*/
public static String extractImageExtension(String imageUrl) {
java.util.regex.Matcher matcher = PATTERN.matcher(imageUrl);
if (matcher.find()) {
String extension = matcher.group(1).toLowerCase();
// 处理jpeg的情况
return "jpeg".equals(extension) ? "jpg" : extension;
}
// 默认后缀
return "png";
}
/**
* 下载图片
* @param url 图片地址
* @return 图片字节数组
*/
public static byte[] downloadImage(String url) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build();
try {
HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() != 200) {
throw new RuntimeException("Failed to download image, status code: " + response.statusCode());
}
return response.body();
} catch (Exception e) {
log.error("Failed to download image: {}", e.getMessage());
return new byte[0];
}
}
/**
* 从URL中提取文件名
*
* @param url 图片URL
* @return 提取的文件名,失败则返回默认名
*/
public static String extractFileNameFromUrl(String url) {
try {
if (url.contains("/")) {
String fileName = url.substring(url.lastIndexOf("/") + 1);
// 如果文件名包含参数,截取?之前的部分
if (fileName.contains("?")) {
fileName = fileName.substring(0, fileName.indexOf("?"));
}
// 如果提取的文件名有效,直接返回
if (fileName.contains(".")) {
return fileName;
}
}
} catch (Exception e) {
log.warn("提取文件名失败,使用默认文件名", e);
}
// 默认文件名
return "upload_" + System.currentTimeMillis() + ".png";
}
}

View File

@@ -13,6 +13,7 @@
<description>第三方内容</description>
<modules>
<module>czg-pay</module>
<module>aggregation-pay</module>
</modules>
<artifactId>cash-sdk</artifactId>

View File

@@ -324,6 +324,15 @@ public class ShopInfoServiceImpl extends ServiceImpl<ShopInfoMapper, ShopInfo> i
return false;
}
@Override
@CacheEvict(key = "#shopId")
public Boolean editEntry(Long shopId, String wechatMerchantId, String alipayMerchantId) {
ShopInfo shopInfo = new ShopInfo();
shopInfo.setWechatMerchantId(wechatMerchantId);
shopInfo.setAlipayMerchantId(alipayMerchantId);
return update(shopInfo, new QueryWrapper().eq(ShopInfo::getId, shopId));
}
@Override
public ShopDetailDTO detail(Long id) throws CzgException {
ShopInfo shopInfo = queryChain().eq(ShopInfo::getId, id == null ? StpKit.USER.getShopId() : id).one();

View File

@@ -0,0 +1,52 @@
package com.czg.service.order.dto;
import com.czg.dto.req.*;
import lombok.Data;
import java.time.LocalDateTime;
/**
* @author ww
*/
@Data
public class AggregateMerchantVO extends AggregateMerchantDto{
private LocalDateTime createTime;
private LocalDateTime updateTime;
private String errorMsg;
/**
* {@link com.czg.PayCst.EntryStatus}
* 微信状态
* WAIT 待提交 INIT 待处理 AUDIT 审核中 SIGN 待签约 REJECTED 失败 FINISH 已完成
*/
private String wechatStatus;
/**
* 微信进件错误信息
*/
private String wechatErrorMsg;
/**
* 微信进件签名地址
*/
private String wechatSignUrl;
/**
* {@link com.czg.PayCst.EntryStatus}
* 支付宝状态
* WAIT 待提交 INIT 待处理 AUDIT 审核中 SIGN 待签约 REJECTED 失败 FINISH 已完成
*/
private String alipayStatus;
/**
* 支付宝进件错误信息
*/
private String alipayErrorMsg;
/**
* 支付宝进件签名地址
*/
private String alipaySignUrl;
}

View File

@@ -0,0 +1,14 @@
package com.czg.service.order.mapper;
import com.mybatisflex.core.BaseMapper;
import com.czg.order.entity.ShopDirectMerchant;
/**
* 商户进件 映射层。
*
* @author ww
* @since 2026-01-07
*/
public interface ShopDirectMerchantMapper extends BaseMapper<ShopDirectMerchant> {
}

View File

@@ -0,0 +1,19 @@
package com.czg.service.order.service;
import com.czg.dto.req.AggregateMerchantDto;
import com.czg.service.order.dto.AggregateMerchantVO;
import com.mybatisflex.core.service.IService;
import com.czg.order.entity.ShopDirectMerchant;
/**
* 商户进件 服务层。
*
* @author ww
* @since 2026-01-07
*/
public interface ShopDirectMerchantService extends IService<ShopDirectMerchant> {
AggregateMerchantVO getEntry(Long shopId);
boolean entryManager(AggregateMerchantDto reqDto);
}

View File

@@ -0,0 +1,133 @@
package com.czg.service.order.service.impl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.unit.DataSizeUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.czg.EntryManager;
import com.czg.PayCst;
import com.czg.config.RabbitPublisher;
import com.czg.dto.req.*;
import com.czg.service.order.dto.AggregateMerchantVO;
import com.czg.utils.FunUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.czg.order.entity.ShopDirectMerchant;
import com.czg.service.order.service.ShopDirectMerchantService;
import com.czg.service.order.mapper.ShopDirectMerchantMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.concurrent.atomic.AtomicLong;
/**
* 商户进件 服务层实现。
*
* @author ww
* @since 2026-01-07
*/
@Service
public class ShopDirectMerchantServiceImpl extends ServiceImpl<ShopDirectMerchantMapper, ShopDirectMerchant> implements ShopDirectMerchantService {
@Resource
private RabbitPublisher rabbitPublisher;
// 全局原子计数器,按天重置(避免数值过大)
private static final AtomicLong COUNTER = new AtomicLong(0);
// 记录上一次的日期,用于重置计数器
private static String LAST_DATE = DateUtil.format(new Date(), "yyyyMMdd");
@Override
public AggregateMerchantVO getEntry(Long shopId) {
ShopDirectMerchant merchant = getById(shopId);
if (merchant == null) {
return null;
}
return convertVO(merchant);
}
@Override
public boolean entryManager(AggregateMerchantDto reqDto) {
boolean isSave = false;
boolean result;
if (StrUtil.isBlank(reqDto.getMerchantCode())) {
reqDto.setMerchantCode(getMerchantCode());
isSave = true;
}
EntryManager.verifyEntryParam(reqDto);
ShopDirectMerchant merchant = new ShopDirectMerchant();
merchant.setShopId(reqDto.getShopId());
merchant.setMerchantCode(reqDto.getMerchantCode());
merchant.setLicenceNo(reqDto.getBusinessLicenceInfo().getLicenceNo());
merchant.setMerchantBaseInfo(JSONObject.toJSONString(reqDto.getMerchantBaseInfo()));
merchant.setLegalPersonInfo(JSONObject.toJSONString(reqDto.getLegalPersonInfo()));
merchant.setBusinessLicenceInfo(JSONObject.toJSONString(reqDto.getBusinessLicenceInfo()));
merchant.setStoreInfo(JSONObject.toJSONString(reqDto.getStoreInfo()));
merchant.setSettlementInfo(JSONObject.toJSONString(reqDto.getSettlementInfo()));
merchant.setWechatStatus(PayCst.EntryStatus.WAIT);
merchant.setAlipayStatus(PayCst.EntryStatus.WAIT);
if (isSave) {
result = save(merchant);
} else {
result = updateById(merchant);
}
//发送进件队列消息
FunUtils.transactionSafeRun(() -> rabbitPublisher.sendEntryManagerMsg(reqDto.getShopId().toString()));
return result;
}
private static String getMerchantCode() {
Date now = new Date();
// 1. 获取当前日期yyyyMMdd
String currentDate = DateUtil.format(now, "yyyyMMdd");
// 2. 每天重置计数器,避免数值溢出
synchronized (COUNTER) {
if (!currentDate.equals(LAST_DATE)) {
COUNTER.set(0);
LAST_DATE = currentDate;
}
}
// 3. 原子递增,获取唯一序号(同一毫秒内不会重复)
long seq = COUNTER.incrementAndGet();
// 4. 时间戳(毫秒级)+ 6位序号补零
String timeStr = DateUtil.format(now, "yyyyMMddHHmmss");
String seqStr = String.format("%03d", seq);
return "CZG" + timeStr + seqStr;
}
public AggregateMerchantVO convertVO(ShopDirectMerchant entity) {
if (entity == null) {
return null;
}
AggregateMerchantVO vo = new AggregateMerchantVO();
vo.setShopId(entity.getShopId());
vo.setMerchantCode(entity.getMerchantCode());
// 解析JSON字段
vo.setMerchantBaseInfo(JSONObject.parseObject(entity.getMerchantBaseInfo(), MerchantBaseInfoDto.class));
vo.setLegalPersonInfo(JSONObject.parseObject(entity.getLegalPersonInfo(), LegalPersonInfoDto.class));
vo.setBusinessLicenceInfo(JSONObject.parseObject(entity.getBusinessLicenceInfo(), BusinessLicenceInfoDto.class));
vo.setStoreInfo(JSONObject.parseObject(entity.getStoreInfo(), StoreInfoDto.class));
vo.setSettlementInfo(JSONObject.parseObject(entity.getSettlementInfo(), SettlementInfoDto.class));
// 设置其他字段
vo.setCreateTime(entity.getCreateTime());
vo.setUpdateTime(entity.getUpdateTime());
vo.setWechatStatus(entity.getWechatStatus());
vo.setWechatErrorMsg(entity.getWechatErrorMsg());
vo.setWechatSignUrl(entity.getWechatSignUrl());
vo.setAlipayStatus(entity.getAlipayStatus());
vo.setAlipayErrorMsg(entity.getAlipayErrorMsg());
vo.setAlipaySignUrl(entity.getAlipaySignUrl());
return vo;
}
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.czg.service.order.mapper.ShopDirectMerchantMapper">
</mapper>

View File

@@ -26,6 +26,11 @@
<artifactId>czg-pay</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.czg</groupId>
<artifactId>aggregation-pay</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,14 @@
package com.czg.service.system.mapper;
import com.mybatisflex.core.BaseMapper;
import com.czg.system.entity.SysBankInfo;
/**
* 银行账户信息表 映射层。
*
* @author ww
* @since 2026-01-06
*/
public interface SysBankInfoMapper extends BaseMapper<SysBankInfo> {
}

View File

@@ -0,0 +1,14 @@
package com.czg.service.system.mapper;
import com.mybatisflex.core.BaseMapper;
import com.czg.system.entity.SysCategoryInfo;
/**
* 类目信息表 映射层。
*
* @author ww
* @since 2026-01-06
*/
public interface SysCategoryInfoMapper extends BaseMapper<SysCategoryInfo> {
}

View File

@@ -0,0 +1,14 @@
package com.czg.service.system.mapper;
import com.mybatisflex.core.BaseMapper;
import com.czg.system.entity.SysRegion;
/**
* 行政区表 映射层。
*
* @author ww
* @since 2026-01-06
*/
public interface SysRegionMapper extends BaseMapper<SysRegion> {
}

View File

@@ -0,0 +1,26 @@
package com.czg.service.system.service.impl;
import com.czg.BaseQueryParam;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.czg.system.entity.SysBankInfo;
import com.czg.system.service.SysBankInfoService;
import com.czg.service.system.mapper.SysBankInfoMapper;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 银行账户信息表 服务层实现。
*
* @author ww
* @since 2026-01-06
*/
@Service
public class SysBankInfoServiceImpl extends ServiceImpl<SysBankInfoMapper, SysBankInfo> implements SysBankInfoService {
@Override
public Page<SysBankInfo> bankInfoList(BaseQueryParam param, String bankName) {
return page(Page.of(param.getPage(), param.getSize()), query().like(SysBankInfo::getBankAlias, bankName));
}
}

View File

@@ -0,0 +1,43 @@
package com.czg.service.system.service.impl;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import com.czg.system.vo.SysCategoryInfoVO;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.czg.system.entity.SysCategoryInfo;
import com.czg.system.service.SysCategoryInfoService;
import com.czg.service.system.mapper.SysCategoryInfoMapper;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 类目信息表 服务层实现。
*
* @author ww
* @since 2026-01-06
*/
@Service
public class SysCategoryInfoServiceImpl extends ServiceImpl<SysCategoryInfoMapper, SysCategoryInfo> implements SysCategoryInfoService {
@Cacheable(value = "common:category", key = "'all'")
@Override
public List<SysCategoryInfoVO> categoryList() {
List<SysCategoryInfo> list = list();
if (CollUtil.isEmpty(list)) {
return List.of();
}
List<SysCategoryInfoVO> result = new ArrayList<>();
Map<String, List<SysCategoryInfo>> stringListMap = CollStreamUtil.groupByKey(list, SysCategoryInfo::getFirstCategory);
stringListMap.forEach((k, v) -> {
SysCategoryInfoVO vo = new SysCategoryInfoVO();
vo.setFirstCategory(k);
vo.setChild(v);
result.add(vo);
});
return result;
}
}

View File

@@ -0,0 +1,56 @@
package com.czg.service.system.service.impl;
import com.czg.service.system.mapper.SysRegionMapper;
import com.czg.system.entity.SysRegion;
import com.czg.system.service.SysRegionService;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 行政区表 服务层实现。
*
* @author ww
* @since 2026-01-06
*/
@Service
public class SysRegionServiceImpl extends ServiceImpl<SysRegionMapper, SysRegion> implements SysRegionService {
@Cacheable(value = "common:region", key = "'all'")
@Override
public List<SysRegion> regionList() {
// 1. 单次查询获取所有数据
List<SysRegion> allRegions = list(query().ne(SysRegion::getRegionLevel, 1));
// 2. 一次性按层级分组,减少流遍历次数
Map<Integer, List<SysRegion>> regionByLevel = allRegions.stream()
.collect(Collectors.groupingBy(SysRegion::getRegionLevel));
// 3. 获取各层级数据,默认空列表避免空指针
List<SysRegion> parents = regionByLevel.getOrDefault(2, List.of());
List<SysRegion> level3Regions = regionByLevel.getOrDefault(3, List.of());
List<SysRegion> level4Regions = regionByLevel.getOrDefault(4, List.of());
// 4. 构建3级地区的子节点映射4级使用HashMap保证性能
Map<String, List<SysRegion>> level4ByParentId = level4Regions.stream()
.collect(Collectors.groupingBy(SysRegion::getParentRegionId, Collectors.toList()));
level3Regions.forEach(level3 -> {
List<SysRegion> children = level4ByParentId.getOrDefault(level3.getRegionId(), List.of());
level3.setChildren(children);
});
Map<String, List<SysRegion>> level3ByParentId = level3Regions.stream()
.collect(Collectors.groupingBy(SysRegion::getParentRegionId));
parents.forEach(parent -> {
List<SysRegion> children = level3ByParentId.getOrDefault(parent.getRegionId(), List.of());
parent.setChildren(children);
});
return parents;
}
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.czg.service.system.mapper.SysBankInfoMapper">
</mapper>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.czg.service.system.mapper.SysCategoryInfoMapper">
</mapper>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.czg.service.system.mapper.SysRegionMapper">
</mapper>