无关风月
6 天以前 4e124926ee6ee682084466067ae37550fca475fa
代码提交 世界杯修改支付宝资金结算功能
16个文件已修改
3个文件已添加
1478 ■■■■ 已修改文件
cloud-server-account/src/main/java/com/dsh/account/util/PayMoneyUtil.java 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-activity/src/main/resources/logback-spring.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-activity/src/main/resources/sharding-jdbc.properties 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/controller/WorldCupController.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/IWorldCupPaymentService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/IWorldCupService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/impl/WorldCupPaymentServiceImpl.java 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/impl/WorldCupServiceImpl.java 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/util/PayMoneyUtil.java 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/util/wx/Create.java 292 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-communityWorldCup/src/main/resources/sharding-jdbc.properties 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-competition/src/main/java/com/dsh/competition/controller/CompetitionController.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-competition/src/main/java/com/dsh/competition/service/impl/PaymentCompetitionServiceImpl.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-competition/src/main/java/com/dsh/competition/util/PayMoneyUtil.java 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-competition/src/main/java/com/dsh/competition/util/wx/Create.java 292 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-other/src/main/java/com/dsh/other/controller/SiteController.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-other/src/main/java/com/dsh/other/service/impl/SiteServiceImpl.java 65 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-other/src/main/java/com/dsh/other/util/PayMoneyUtil.java 43 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-other/src/main/java/com/dsh/other/util/wx/Create.java 300 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cloud-server-account/src/main/java/com/dsh/account/util/PayMoneyUtil.java
@@ -377,19 +377,19 @@
        }
        return ResultUtil.success(response.getBody());
    }
    public static void main(String[] args) throws AlipayApiException {
        confirm9("2088670241691219", "20250706132544057730", "2025070622001467281419691745", "0.1");
        confirm9("2088670241691219", "20250706132448647822", "2025070622001467281421616221", "0.1");
        confirm9("2088670241691219", "20250706132703365228", "2025070622001467281418986854", "0.1");
        confirm9("2088670241691219", "20250706132812692528", "2025070622001467281421628990", "0.1");
        confirm9("2088670241691219", "20250706133122769531", "2025070622001467281421682297", "0.1");
        confirm9("2088670241691219", "20250706155632714326", "2025070622001423881412880254", "0.01");
        confirm9("2088670241691219", "20250707124157706846", "2025070722001479841407246616", "0.01");
        confirm9("2088670241691219", "20250709182513843937", "2025070922001467281434141170", "0.01");
        confirm9("2088670241691219", "20250709183148107861", "2025070922001467281433375653", "0.01");
        confirm9("2088670241691219", "20250715203444033496", "2025071522001457851416341513", "5");
    }
//    public static void main(String[] args) throws AlipayApiException {
//        confirm9("2088670241691219", "20250706132544057730", "2025070622001467281419691745", "0.1");
//        confirm9("2088670241691219", "20250706132448647822", "2025070622001467281421616221", "0.1");
//        confirm9("2088670241691219", "20250706132703365228", "2025070622001467281418986854", "0.1");
//        confirm9("2088670241691219", "20250706132812692528", "2025070622001467281421628990", "0.1");
//        confirm9("2088670241691219", "20250706133122769531", "2025070622001467281421682297", "0.1");
//        confirm9("2088670241691219", "20250706155632714326", "2025070622001423881412880254", "0.01");
//        confirm9("2088670241691219", "20250707124157706846", "2025070722001479841407246616", "0.01");
//        confirm9("2088670241691219", "20250709182513843937", "2025070922001467281434141170", "0.01");
//        confirm9("2088670241691219", "20250709183148107861", "2025070922001467281433375653", "0.01");
//        confirm9("2088670241691219", "20250715203444033496", "2025071522001457851416341513", "5");
//
//    }
    private static AlipayConfig getAlipayConfig() {
        String privateKey  = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCi5i9nW/hGLJ3A06cZxTQdviFC7THpdSihoTYGLr9q006hu0V26ecBMY/o4w5bvIX0Ok/yofmZsVcCJpAPvbXL/uqVrIjnRRxXiaeBFThlxoBUTdunvbUSDYfzlEhJr5NvUKI6H6lz2niXlQGx4qy8Hau4ccWit9kM8jwUvsBVQoFgJA+xrjMvooA7YLopQtpOD+UJr5thApTSf1xrnr1W12yolTLEH15JmNV372cqXrYUuqnY0QsaPtxeqJUGAOcGdVLllQ7easEznP8DFBvDdHATcmp2SHNQDUEWN6MCVPbMgY06NQVqAXxqjTAYSVh+6TRu6bofPmpYC3TZB003AgMBAAECggEBAJAcR2+PA3NBYUYHeFrqBRMS8uX8ZR19kjZ7IgoSLTFaQsP9opRylPSPXhrPVBKAE5leRQAHn4MCSlESwHvMfxo7KFjFTFAc6dffZZpipYQUOc9bGampwJh58/3e/pyBgVMG6J23CPf/HJQtNFSkjd/V9+ayb/9l2dUEL3bC0fAZ/dbx8HsxdLw8wn3fLlWLj68hOMqa2deCZe3JdSVsPbeWqkh56FFsMLug0Nd+Ar4TgRl9/jnhXF0JWiD0LmPUYLhboY7EfUBzN4w1iYbDi1P+3zvoOYsiVKAXox9GMhQ2VzOO2UcSTuizSza2e98mGpabl/GpKmCz+RDFjtkX6eECgYEA2MyCij65eO3aGIm3FUe93DULRBYTfX8qJQSJq2WOWA3mmQlEW6L3O2B5/lG2h+8WmN6iLEs9eHpgycGYp7vAqgrANEn16ACVcuyx0scFtrZfZ+kmHMzFfiUWxJjVYk/6YngsGVBLdw6ueM42C8TTP67X9tU5TdVGoGWuqEj4W98CgYEAwFqwprXOch5Pqk/RPbb49r0Ou03K/UbciWnWWKzUhFFNS8MdlQPoDvQZbMwHLeWsa2VhaKITK3x5biLQb3U+0GLOn6lTvEyrEUH+ucREyLgVYTRAvwBPtnvlrzpyxPk2HnslQjju8WrvvLLBMKWUjlTrTOzhaHT21gz3pHMiOakCgYEAhLmfaXdBITGshb054sNLDtdCkGpbgEcrzAHdLps769iGxkYQHXHFngpQZUwtTUcoNGqIKknd1jZFrv7gsD+XkgKG7PwimehRlkwmCX5ilxtLiVgJRzRt6+5U5AMVD90a0tHzXYP0z2yjj73fBJF5KtGl0a10KZxaYrQdm1UhB00CgYBZZgzx/k9rtHC8LAqIj1CYhHejT92G53c6Gkl3vyOqN4sgKhfGmSEySfrDGPRBPZxr8ZtbIPCd5mUdberH0osWGMYFaJI1UsCy7aQwvGpniz7MhZeN7dweaOjwDs8mgtjHQ96mL4XGCDhR0BZ/wIURvZ/6iaGdhbbu9unlsWj3uQKBgQCmZYdsbbZkd3ev6f8rwyvMz+DrCQyYpY44cegBYuJgrZiQnL2fJioeN7ixX0UM48SfwsZEIrzshP/LGAwnc2MdjxKUl4jLN8SEe0NAjXOnz9Zaw740+aOmLpXcLWdP4uM2gIhWsvW1tEkQZCXmm7c9s/RsU8Pmzv+YL3+fSijOzA==";
        String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmu8n/4yTHWbn7VOrNc9OsLtDL1bEQ8gC1dHkj8Wy5z0mkaOsjJRIG/28ze12M0V8jdCKuuDr5Z1OPKiqf+XO3ypguEh+mYUVMBM/cZodDFQfTY1TKLWjvQCuaqlA+QUTCK6f7T7stsgyQ1o9Jj0rXZDz6PM4QHSTzjrLIBaeqM5WIBvH+fy/X+QG5Utd+/UT0kc0JyvuKhZ65yVUd/C9VcwJJAPliRsAQNrqYterwAJ9zvw9tF11wj9W0XgJ8Ccu4x3gR1vrlLRJJo/OA97RmxPQ+5hSacWQZCUd1dwiBq+YCrKVHGTj14izRHXrLc0yBlRXo7tBOIqcy3IsvKVthQIDAQAB";
cloud-server-activity/src/main/resources/logback-spring.xml
@@ -24,7 +24,7 @@
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
            <level>info</level>
        </filter>
        <encoder>
            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
cloud-server-activity/src/main/resources/sharding-jdbc.properties
@@ -14,7 +14,7 @@
datasource.names=master0
datasource.master0.type=com.alibaba.druid.pool.DruidDataSource
datasource.master0.driverClassName=com.mysql.cj.jdbc.Driver
datasource.master0.url=jdbc:mysql://127.0.0.1:3306/playpai_activity?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
datasource.master0.url=jdbc:mysql://127.0.0.1/playpai_activity?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
datasource.master0.username=root
datasource.master0.password=playpai2023!
datasource.master0.maxActive=20
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/controller/WorldCupController.java
@@ -106,7 +106,6 @@
    /**
     * 查询社区世界杯收入--管理后台
     * @param storeId
     * @return
     */
    @ResponseBody
@@ -654,7 +653,7 @@
            @ApiImplicitParam(name = "id", value = "列表中的id", required = true, dataType = "Long"),
            @ApiImplicitParam(name = "Authorization", value = "用户token(Bearer +token)", required = true, dataType = "String", paramType = "header", defaultValue = "Bearer eyJhbGciOiJIUzUxMiJ9.....")
    })
    public ResultUtil cancelMyWorldCup(Long id){
    public ResultUtil cancelMyWorldCup(Long id) throws Exception {
        return worldCupPaymentService.cancelMyWorldCup(id);
    }
@@ -942,7 +941,7 @@
     */
    @ResponseBody
    @PostMapping("/worldCup/cancelWorldCupRefund")
    public void cancelWorldCupRefund(@RequestBody Integer id){
    public void cancelWorldCupRefund(@RequestBody Integer id) throws Exception {
        worldCupService.cancelWorldCupRefund(id);
    }
@@ -988,6 +987,54 @@
            out.close();
        }
    }
    /**
     * 取消赛事后微信退款回调微信V3回调
     * @param request
     * @param response
     */
    @ResponseBody
    @PostMapping("/base/worldCup/wxRefundWorldCupCallback1")
    public void wxRefundWorldCupCallback1(HttpServletRequest request, HttpServletResponse response){
        try {
            System.err.println("微信回调");
            System.err.println("请求" + request);
            BufferedReader reader = request.getReader();
            String string1 = reader.toString();
            System.err.println("请求reader" + string1);
            StringBuilder requestBody = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                requestBody.append(line);
            }
            System.err.println("全部请求体" + requestBody);
            JSONObject jsonObject = JSONObject.parseObject(requestBody.toString());
            JSONObject resource = jsonObject.getJSONObject("resource");
            AesUtil aesUtil = new AesUtil(WxV3PayConfig.apiV3Key.getBytes(StandardCharsets.UTF_8));
            String decryptedData = aesUtil.decryptToString(resource.getString("associated_data").getBytes(StandardCharsets.UTF_8), resource.getString("nonce").getBytes(StandardCharsets.UTF_8),
                    resource.getString("ciphertext"));
            System.err.println("微信解密的字符串信息" + decryptedData);
            JSONObject jsonInfo = (JSONObject) JSONObject.parse(decryptedData);
            String code = jsonInfo.getString("out_trade_no");
            String transaction_id = jsonInfo.getString("transaction_id");
            String refund_status = jsonInfo.getString("refund_status");
            String out_refund_no = jsonInfo.getString("out_refund_no");
            if (refund_status.equals("SUCCESS")) {
                WorldCupPayment worldCupPayment = worldCupPaymentService.getOne(new QueryWrapper<WorldCupPayment>().eq("code", out_refund_no));
                worldCupPayment.setRefundOrderNo(code);
                worldCupPayment.setRefundTime(new Date());
                worldCupPayment.setPayStatus(3);
                worldCupPayment.setWorldCupId(null);
                worldCupPaymentService.updateById(worldCupPayment);
                PrintWriter out = response.getWriter();
                out.write("SUCCESS");
                out.flush();
                out.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/IWorldCupPaymentService.java
@@ -16,5 +16,5 @@
     * @param id
     * @return
     */
    ResultUtil cancelMyWorldCup(Long id);
    ResultUtil cancelMyWorldCup(Long id) throws Exception;
}
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/IWorldCupService.java
@@ -85,7 +85,7 @@
     * 取消赛事后退还费用
     * @param id
     */
    void cancelWorldCupRefund(Integer id);
    void cancelWorldCupRefund(Integer id) throws Exception;
    /**
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/impl/WorldCupPaymentServiceImpl.java
@@ -7,23 +7,30 @@
import com.dsh.communityWorldCup.entity.WorldCup;
import com.dsh.communityWorldCup.entity.WorldCupPayment;
import com.dsh.communityWorldCup.entity.WorldCupPaymentParticipant;
import com.dsh.communityWorldCup.entity.WorldCupStore;
import com.dsh.communityWorldCup.feignclient.account.AppUserClient;
import com.dsh.communityWorldCup.feignclient.account.model.AppUser;
import com.dsh.communityWorldCup.feignclient.course.CoursePackageOrderStudentClient;
import com.dsh.communityWorldCup.feignclient.course.model.CoursePackageOrderStudent;
import com.dsh.communityWorldCup.feignclient.other.StoreClient;
import com.dsh.communityWorldCup.feignclient.other.model.Store;
import com.dsh.communityWorldCup.mapper.WorldCupPaymentMapper;
import com.dsh.communityWorldCup.model.DeductionClassHourList;
import com.dsh.communityWorldCup.service.IWorldCupPaymentParticipantService;
import com.dsh.communityWorldCup.service.IWorldCupPaymentService;
import com.dsh.communityWorldCup.service.IWorldCupService;
import com.dsh.communityWorldCup.service.IWorldCupStoreService;
import com.dsh.communityWorldCup.util.PayMoneyUtil;
import com.dsh.communityWorldCup.util.ResultUtil;
import com.dsh.communityWorldCup.util.UUIDUtil;
import net.bytebuddy.asm.Advice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -37,6 +44,8 @@
    @Autowired
    private IWorldCupPaymentParticipantService worldCupPaymentParticipantService;
    @Autowired
    private IWorldCupStoreService worldCupStoreService;
    @Autowired
    private IWorldCupService worldCupService;
@@ -46,6 +55,8 @@
    @Resource
    private AppUserClient appUserClient;
    @Resource
    private StoreClient storeClient;
    @Resource
    private CoursePackageOrderStudentClient coursePackageOrderStudentClient;
@@ -59,7 +70,7 @@
     * @return
     */
    @Override
    public ResultUtil cancelMyWorldCup(Long id) {
    public ResultUtil cancelMyWorldCup(Long id) throws Exception {
        WorldCupPaymentParticipant worldCupPaymentParticipant = worldCupPaymentParticipantService.getById(id);
        WorldCupPayment worldCupPayment = this.getById(worldCupPaymentParticipant.getWorldCupPaymentId());
        WorldCup worldCup = worldCupService.getById(worldCupPayment.getWorldCupId());
@@ -82,17 +93,32 @@
        //总排款金额(排除已经参加比赛的数据)
        BigDecimal multiply = worldCupPayment.getUnitPrice().multiply(new BigDecimal(list1.size()));
        //微信支付
        List<WorldCupStore> list = worldCupStoreService.lambdaQuery().eq(WorldCupStore::getWorldCupId, worldCup.getId()).list();
        Integer storeId = list.get(0).getStoreId();
        Store store = storeClient.queryStoreById(storeId);
        if(worldCupPayment.getPayType() == 1){
            Map<String, String> map = payMoneyUtil.wxRefund(worldCupPayment.getPayOrderNo(), worldCupPayment.getCode(),
                    worldCupPayment.getAmount().toString(), multiply.toString(), "/base/worldCup/wxRefundWorldCupCallback");
            if(!"SUCCESS".equals(map.get("return_code"))){
                System.err.println("-------------微信退款失败---------");
                System.err.println(map.get("return_msg"));
                return ResultUtil.error("微信退款失败");
            }else{
                worldCupPayment.setRefundAmount(multiply);
                worldCupPayment.setWorldCupId(null);
                this.updateById(worldCupPayment);
            if (store.getOperatorId()!=null && store.getOperatorId()!=0){
                String smidVx= storeClient.getmerchantNumberByOperatorId(store.getOperatorId());
                if (!StringUtils.hasLength(smidVx)){
                    System.err.println("运营商未配置微信商户号,获取支付失败!");
                }
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
                String codeRefund = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);// 退款单号
                // 运营商退款
                payMoneyUtil.weixinRefundV3(smidVx, codeRefund, worldCupPayment.getPayOrderNo(), worldCupPayment.getAmount().toString(), "/base/worldCup/wxRefundWorldCupCallback1");
            }else {
                Map<String, String> map = payMoneyUtil.wxRefund(worldCupPayment.getPayOrderNo(), worldCupPayment.getCode(),
                        worldCupPayment.getAmount().toString(), multiply.toString(), "/base/worldCup/wxRefundWorldCupCallback");
                if (!"SUCCESS".equals(map.get("return_code"))) {
                    System.err.println("-------------微信退款失败---------");
                    System.err.println(map.get("return_msg"));
                    return ResultUtil.error("微信退款失败");
                } else {
                    worldCupPayment.setRefundAmount(multiply);
                    worldCupPayment.setWorldCupId(null);
                    this.updateById(worldCupPayment);
                }
            }
        }
        //支付宝支付
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/service/impl/WorldCupServiceImpl.java
@@ -849,10 +849,13 @@
     * @param id
     */
    @Override
    public void cancelWorldCupRefund(Integer id) {
    public void cancelWorldCupRefund(Integer id) throws Exception {
        //免费除外
        List<WorldCupPayment> list2 = worldCupPaymentService.list(new QueryWrapper<WorldCupPayment>().eq("worldCupId", id)
                .ne("payType", 0).eq("payStatus", 2).eq("state", 1));
        List<WorldCupStore> list = worldCupStoreService.lambdaQuery().eq(WorldCupStore::getWorldCupId, id).list();
        Integer storeId = list.get(0).getStoreId();
        Store store = storeClient.queryStoreById(storeId);
        for (WorldCupPayment worldCupPayment : list2) {
            List<WorldCupPaymentParticipant> list1 = worldCupPaymentParticipantService.list(new QueryWrapper<WorldCupPaymentParticipant>()
                    .eq("worldCupId", id).eq("worldCupPaymentId", worldCupPayment.getId()).eq("alreadyEntered", 0));
@@ -860,14 +863,25 @@
            BigDecimal multiply = worldCupPayment.getUnitPrice().multiply(new BigDecimal(list1.size()));
            //微信支付
            if(worldCupPayment.getPayType() == 1){
                Map<String, String> map = payMoneyUtil.wxRefund(worldCupPayment.getPayOrderNo(), worldCupPayment.getCode(),
                        worldCupPayment.getAmount().toString(), multiply.toString(), "/base/worldCup/wxRefundWorldCupCallback");
                if(!"SUCCESS".equals(map.get("return_code"))){
                    System.err.println("-------------微信退款失败---------");
                    System.err.println(map.get("return_msg"));
                }else{
                    worldCupPayment.setRefundAmount(multiply);
                    worldCupPaymentService.updateById(worldCupPayment);
                if (store.getOperatorId()!=null && store.getOperatorId()!=0){
                    String smidVx= storeClient.getmerchantNumberByOperatorId(store.getOperatorId());
                    if (!StringUtils.hasLength(smidVx)){
                        System.err.println("运营商未配置微信商户号,获取支付失败!");
                    }
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
                    String codeRefund = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);// 退款单号
                    // 运营商退款
                    payMoneyUtil.weixinRefundV3(smidVx, codeRefund, worldCupPayment.getPayOrderNo(), worldCupPayment.getAmount().toString(), "/base/worldCup/wxRefundWorldCupCallback1");
                }else {
                    Map<String, String> map = payMoneyUtil.wxRefund(worldCupPayment.getPayOrderNo(), worldCupPayment.getCode(),
                            worldCupPayment.getAmount().toString(), multiply.toString(), "/base/worldCup/wxRefundWorldCupCallback");
                    if (!"SUCCESS".equals(map.get("return_code"))) {
                        System.err.println("-------------微信退款失败---------");
                        System.err.println(map.get("return_msg"));
                    } else {
                        worldCupPayment.setRefundAmount(multiply);
                        worldCupPaymentService.updateById(worldCupPayment);
                    }
                }
            }
            //支付宝支付
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/util/PayMoneyUtil.java
@@ -7,10 +7,7 @@
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.dsh.communityWorldCup.util.httpClinet.HttpClientUtil;
import com.dsh.communityWorldCup.util.wx.PartnerAppPrepay;
import com.dsh.communityWorldCup.util.wx.WXPayUtility;
import com.dsh.communityWorldCup.util.wx.WeChatV3SignUtil;
import com.dsh.communityWorldCup.util.wx.WxV3PayConfig;
import com.dsh.communityWorldCup.util.wx.*;
import org.apache.commons.collections.map.HashedMap;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.dom4j.Document;
@@ -729,6 +726,39 @@
        }
    }
    public ResultUtil weixinRefundV3(String subMchid,String outRefundNo, String transactionId, String totalFee, String notifyUrl) throws Exception {
        BigDecimal bigDecimal = new BigDecimal(totalFee);
        int i = bigDecimal.multiply(new BigDecimal("100")).intValue();
        // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/partner/4013080340
        Create client = new Create(
                "1681873607",                    // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
                "55714944F7A7E52526F708280B176DCC838F371A",         // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
                "/usr/playpai/server/wxV3/1681873607_20250424_cert/apiclient_key.pem",    // 商户API证书私钥文件路径,本地文件路径
                "PUB_KEY_ID_0116818736072025042400351694002605",      // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
                "/usr/playpai/server/wxV3/pub_key.pem"        // 微信支付公钥文件路径,本地文件路径
        );
        Create.CreateRequest request = new Create.CreateRequest();
        request.subMchid = subMchid;
        request.transactionId = transactionId;
        request.outRefundNo = outRefundNo;
        request.reason = "退款";
        request.notifyUrl = callbackPath+notifyUrl;
        request.amount = new Create.AmountReq();
        request.amount.refund = (long) i;
        request.amount.total = (long) i;
        request.amount.currency = "CNY";
        try {
            Create.Refund response = client.run(request);
            // TODO: 请求成功,继续业务逻辑
            System.out.println(response);
        } catch (WXPayUtility.ApiException e) {
            // TODO: 请求失败,根据状态码执行不同的逻辑
            e.printStackTrace();
        }
        return ResultUtil.success();
    }
    /**
     * 微信退款成功后的回调处理
cloud-server-communityWorldCup/src/main/java/com/dsh/communityWorldCup/util/wx/Create.java
New file
@@ -0,0 +1,292 @@
package com.dsh.communityWorldCup.util.wx;
import com.google.gson.annotations.SerializedName;
import okhttp3.*;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.List;
/**
 * 退款申请
 */
public class Create {
  private static String HOST = "https://api.mch.weixin.qq.com";
  private static String METHOD = "POST";
  private static String PATH = "/v3/refund/domestic/refunds";
  public Refund run(CreateRequest request) {
    String uri = PATH;
    String reqBody = WXPayUtility.toJson(request);
    Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
    reqBuilder.addHeader("Accept", "application/json");
    reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
    reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo, privateKey, METHOD, uri, reqBody));
    reqBuilder.addHeader("Content-Type", "application/json");
    RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
    reqBuilder.method(METHOD, requestBody);
    Request httpRequest = reqBuilder.build();
    // 发送HTTP请求
    OkHttpClient client = new OkHttpClient.Builder().build();
    try (Response httpResponse = client.newCall(httpRequest).execute()) {
      String respBody = WXPayUtility.extractBody(httpResponse);
      if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
        // 2XX 成功,验证应答签名
        WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
                                      httpResponse.headers(), respBody);
        // 从HTTP应答报文构建返回数据
        return WXPayUtility.fromJson(respBody, Refund.class);
      } else {
        throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
      }
    } catch (IOException e) {
      throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
    }
  }
  private final String mchid;
  private final String certificateSerialNo;
  private final PrivateKey privateKey;
  private final String wechatPayPublicKeyId;
  private final PublicKey wechatPayPublicKey;
  public Create(String mchid, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) {
    this.mchid = mchid;
    this.certificateSerialNo = certificateSerialNo;
    this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyFilePath);
    this.wechatPayPublicKeyId = wechatPayPublicKeyId;
    this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath);
  }
  public enum Status {
    @SerializedName("SUCCESS")
    SUCCESS,
    @SerializedName("CLOSED")
    CLOSED,
    @SerializedName("PROCESSING")
    PROCESSING,
    @SerializedName("ABNORMAL")
    ABNORMAL
  }
  public enum Account {
    @SerializedName("AVAILABLE")
    AVAILABLE,
    @SerializedName("UNAVAILABLE")
    UNAVAILABLE
  }
  public enum PromotionType {
    @SerializedName("COUPON")
    COUPON,
    @SerializedName("DISCOUNT")
    DISCOUNT
  }
  public static class GoodsDetail {
    @SerializedName("merchant_goods_id")
    public String merchantGoodsId;
    @SerializedName("wechatpay_goods_id")
    public String wechatpayGoodsId;
    @SerializedName("goods_name")
    public String goodsName;
    @SerializedName("unit_price")
    public Long unitPrice;
    @SerializedName("refund_amount")
    public Long refundAmount;
    @SerializedName("refund_quantity")
    public Integer refundQuantity;
  }
  public static class CreateRequest {
    @SerializedName("sub_mchid")
    public String subMchid;
    @SerializedName("transaction_id")
    public String transactionId;
    @SerializedName("out_trade_no")
    public String outTradeNo;
    @SerializedName("out_refund_no")
    public String outRefundNo;
    @SerializedName("reason")
    public String reason;
    @SerializedName("notify_url")
    public String notifyUrl;
    @SerializedName("funds_account")
    public ReqFundsAccount fundsAccount;
    @SerializedName("amount")
    public AmountReq amount;
    @SerializedName("goods_detail")
    public List<GoodsDetail> goodsDetail;
  }
  public enum Channel {
    @SerializedName("ORIGINAL")
    ORIGINAL,
    @SerializedName("BALANCE")
    BALANCE,
    @SerializedName("OTHER_BALANCE")
    OTHER_BALANCE,
    @SerializedName("OTHER_BANKCARD")
    OTHER_BANKCARD
  }
  public static class Amount {
    @SerializedName("total")
    public Long total;
    @SerializedName("refund")
    public Long refund;
    @SerializedName("from")
    public List<FundsFromItem> from;
    @SerializedName("payer_total")
    public Long payerTotal;
    @SerializedName("payer_refund")
    public Long payerRefund;
    @SerializedName("settlement_refund")
    public Long settlementRefund;
    @SerializedName("settlement_total")
    public Long settlementTotal;
    @SerializedName("discount_refund")
    public Long discountRefund;
    @SerializedName("currency")
    public String currency;
    @SerializedName("refund_fee")
    public Long refundFee;
  }
  public enum ReqFundsAccount {
    @SerializedName("AVAILABLE")
    AVAILABLE,
    @SerializedName("UNSETTLED")
    UNSETTLED
  }
  public enum FundsAccount {
    @SerializedName("UNSETTLED")
    UNSETTLED,
    @SerializedName("AVAILABLE")
    AVAILABLE,
    @SerializedName("UNAVAILABLE")
    UNAVAILABLE,
    @SerializedName("OPERATION")
    OPERATION,
    @SerializedName("BASIC")
    BASIC,
    @SerializedName("ECNY_BASIC")
    ECNY_BASIC
  }
  public static class Promotion {
    @SerializedName("promotion_id")
    public String promotionId;
    @SerializedName("scope")
    public PromotionScope scope;
    @SerializedName("type")
    public PromotionType type;
    @SerializedName("amount")
    public Long amount;
    @SerializedName("refund_amount")
    public Long refundAmount;
    @SerializedName("goods_detail")
    public List<GoodsDetail> goodsDetail;
  }
  public static class Refund {
    @SerializedName("refund_id")
    public String refundId;
    @SerializedName("out_refund_no")
    public String outRefundNo;
    @SerializedName("transaction_id")
    public String transactionId;
    @SerializedName("out_trade_no")
    public String outTradeNo;
    @SerializedName("channel")
    public Channel channel;
    @SerializedName("user_received_account")
    public String userReceivedAccount;
    @SerializedName("success_time")
    public String successTime;
    @SerializedName("create_time")
    public String createTime;
    @SerializedName("status")
    public Status status;
    @SerializedName("funds_account")
    public FundsAccount fundsAccount;
    @SerializedName("amount")
    public Amount amount;
    @SerializedName("promotion_detail")
    public List<Promotion> promotionDetail;
  }
  public static class FundsFromItem {
    @SerializedName("account")
    public Account account;
    @SerializedName("amount")
    public Long amount;
  }
  public static class AmountReq {
    @SerializedName("refund")
    public Long refund;
    @SerializedName("from")
    public List<FundsFromItem> from;
    @SerializedName("total")
    public Long total;
    @SerializedName("currency")
    public String currency;
  }
  public enum PromotionScope {
    @SerializedName("GLOBAL")
    GLOBAL,
    @SerializedName("SINGLE")
    SINGLE
  }
}
cloud-server-communityWorldCup/src/main/resources/sharding-jdbc.properties
@@ -1,15 +1,3 @@
datasource.names=master0
datasource.master0.type=com.alibaba.druid.pool.DruidDataSource
datasource.master0.driverClassName=com.mysql.cj.jdbc.Driver
datasource.master0.url=jdbc:mysql://8.137.22.229:3306/playpai_community_world_cup?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
datasource.master0.username=root
datasource.master0.password=playpai2023!
datasource.master0.maxActive=20
datasource.master0.maxWait=60000
datasource.master0.minIdle=5
datasource.master0.initialSize=2
#datasource.names=master0
#datasource.master0.type=com.alibaba.druid.pool.DruidDataSource
@@ -23,4 +11,13 @@
#datasource.master0.initialSize=2
datasource.names=master0
datasource.master0.type=com.alibaba.druid.pool.DruidDataSource
datasource.master0.driverClassName=com.mysql.cj.jdbc.Driver
datasource.master0.url=jdbc:mysql://127.0.0.1:3306/playpai_community_world_cup?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
datasource.master0.username=root
datasource.master0.password=playpai2023!
datasource.master0.maxActive=20
datasource.master0.maxWait=60000
datasource.master0.minIdle=5
datasource.master0.initialSize=2
cloud-server-competition/src/main/java/com/dsh/competition/controller/CompetitionController.java
@@ -21,6 +21,7 @@
import com.dsh.competition.feignclient.course.model.PaymentDeductionClassHour;
import com.dsh.competition.feignclient.model.*;
import com.dsh.competition.feignclient.other.StoreClient;
import com.dsh.competition.feignclient.other.model.Store;
import com.dsh.competition.model.*;
import com.dsh.competition.service.CompetitionService;
import com.dsh.competition.service.IParticipantService;
@@ -667,6 +668,64 @@
            e.printStackTrace();
        }
    }
    /**
     * 取消已报名赛事后微信回退金额回调微信V3服务商版本
     *
     * @param request
     * @param response
     */
    @ResponseBody
    @PostMapping("/base/competition/weChatCancelPaymentCompetitionCallback1")
    public void weChatCancelPaymentCompetitionCallback1(HttpServletRequest request, HttpServletResponse response) {
        try {
            System.err.println("微信回调");
            System.err.println("请求" + request);
            BufferedReader reader = request.getReader();
            String string1 = reader.toString();
            System.err.println("请求reader" + string1);
            StringBuilder requestBody = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                requestBody.append(line);
            }
            System.err.println("全部请求体" + requestBody);
            JSONObject jsonObject = JSONObject.parseObject(requestBody.toString());
            JSONObject resource = jsonObject.getJSONObject("resource");
            AesUtil aesUtil = new AesUtil(WxV3PayConfig.apiV3Key.getBytes(StandardCharsets.UTF_8));
            String decryptedData = aesUtil.decryptToString(resource.getString("associated_data").getBytes(StandardCharsets.UTF_8), resource.getString("nonce").getBytes(StandardCharsets.UTF_8),
                    resource.getString("ciphertext"));
            System.err.println("微信解密的字符串信息" + decryptedData);
            JSONObject jsonInfo = (JSONObject) JSONObject.parse(decryptedData);
            String code = jsonInfo.getString("out_trade_no");
            String transaction_id = jsonInfo.getString("transaction_id");
            String refund_status = jsonInfo.getString("refund_status");
            String out_refund_no = jsonInfo.getString("out_refund_no");
            if (refund_status.equals("SUCCESS")) {
                PaymentCompetition paymentCompetition = paymentCompetitionService.getOne(new QueryWrapper<PaymentCompetition>()
                        .eq("code", code).eq("payType", 1));
                if (paymentCompetition.getPayStatus() == 2) {
                    paymentCompetition.setPayStatus(3);
                    paymentCompetition.setRefundTime(new Date());
                    paymentCompetition.setRefundOrderNo(out_refund_no);
                    paymentCompetition.setAppUserId(null);
                    paymentCompetitionService.updateById(paymentCompetition);
                    Competition competition = cttService.getById(paymentCompetition.getCompetitionId());
                    competition.setApplicantsNumber(competition.getApplicantsNumber() - 1);
                    cttService.updateById(competition);
                    storeClient.addBackRecord(paymentCompetition.getAmount() + "_" + paymentCompetition.getAppUserId());
                }
                PrintWriter out = response.getWriter();
                out.write("SUCCESS");
                out.flush();
                out.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @PostMapping("/base/competition/queryById")
@@ -859,46 +918,32 @@
                String code = paymentCompetition.getCode();
                Double amount = paymentCompetition.getAmount();
                Competition competition = competitionService.getById(paymentCompetition.getCompetitionId());
                Store store = storeClient.queryStoreById(Integer.valueOf(competition.getStoreId().split(",")[0]));
                String payOrderNo = paymentCompetition.getPayOrderNo();
                if (paymentCompetition.getPayType() == 1) {//微信支付
                    if (StringUtils.hasLength(paymentCompetition.getFenzhangNo())){
                        // 是分账订单 如果分账金额不为0 那么回退分账金额
                        if (paymentCompetition.getFenzhangAmount()!=null && paymentCompetition.getFenzhangAmount().compareTo(BigDecimal.ZERO)>0){
                            String randomCode = UUIDUtil.getRandomCode(16);
                            String randomCode1 = UUIDUtil.getRandomCode(16);
                            if (competition.getOperatorId()!=null && competition.getOperatorId()!=0){
                                // 微信商户号
                                String s2 =storeClient.getmerchantNumberByOperatorId(competition.getOperatorId());
                                ResultUtil resultUtil = payMoneyUtil.fenzhangRefund(paymentCompetition.getFenzhangNo(), paymentCompetition.getFenzhangAmount(), s2, randomCode, randomCode1);
                                if (!resultUtil.getCode().equals(200)){
                                    System.err.println("分账回退失败 原因是:"+resultUtil.getMsg());
                                }else{
                                    paymentCompetition.setFenzhangRefundNo(resultUtil.getData().toString());
                                    paymentCompetitionService.updateById(paymentCompetition);
                                }
                            }
                    if (store.getOperatorId()!=null && store.getOperatorId()!=0){
                        String smidVx= storeClient.getmerchantNumberByOperatorId(store.getOperatorId());
                        if (!StringUtils.hasLength(smidVx)){
                            System.err.println("运营商未配置微信商户号,获取支付失败!");
                        }
                    }
                    Map<String, String> map = payMoneyUtil.wxRefund(payOrderNo, code, amount.toString(), amount.toString(),
                            "/base/competition/weChatCancelPaymentCompetitionCallback");
                    String return_code = map.get("return_code");
                    if (!"SUCCESS".equals(return_code)) {
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
                        String codeRefund = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);// 退款单号
                        // 运营商退款
                        payMoneyUtil.weixinRefundV3(smidVx, codeRefund, pay.getPayOrderNo(), pay.getAmount().toString(), "/base/competition/weChatCancelPaymentCompetitionCallback1");
                    }else {
                        Map<String, String> map = payMoneyUtil.wxRefund(payOrderNo, code, amount.toString(), amount.toString(),
                                "/base/competition/weChatCancelPaymentCompetitionCallback");
                        String return_code = map.get("return_code");
                        if (!"SUCCESS".equals(return_code)) {
//                        return ResultUtil.error(map.get("return_msg"));
                        continue;
                            continue;
                        }
                        String refund_id = map.get("refund_id");
                        paymentCompetition.setRefundOrderNo(refund_id);
                        paymentCompetition.setAppUserId(null);
                        paymentCompetitionService.updateById(paymentCompetition);
                        storeClient.addBackRecord(paymentCompetition.getAmount() + "_" + paymentCompetition.getAppUserId());
                    }
                    String refund_id = map.get("refund_id");
                    paymentCompetition.setRefundOrderNo(refund_id);
                    paymentCompetition.setAppUserId(null);
                    paymentCompetitionService.updateById(paymentCompetition);
                    storeClient.addBackRecord(paymentCompetition.getAmount() + "_" + paymentCompetition.getAppUserId());
                }
                if (paymentCompetition.getPayType() == 2) {//支付宝支付
                    Map<String, String> map = payMoneyUtil.aliRefund(payOrderNo, amount.toString());
cloud-server-competition/src/main/java/com/dsh/competition/service/impl/PaymentCompetitionServiceImpl.java
@@ -248,38 +248,32 @@
        }
        String payOrderNo = paymentCompetition.getPayOrderNo();
        Store store = storeClient.queryStoreById(Integer.valueOf(competition.getStoreId().split(",")[0]));
        if (paymentCompetition.getPayType() == 1) {//微信支付
            if (StringUtils.hasLength(paymentCompetition.getFenzhangNo())){
                // 是分账订单 如果分账金额不为0 那么回退分账金额
                if (paymentCompetition.getFenzhangAmount()!=null && paymentCompetition.getFenzhangAmount().compareTo(BigDecimal.ZERO)>0){
                    String randomCode = UUIDUtil.getRandomCode(16);
                    String randomCode1 = UUIDUtil.getRandomCode(16);
                    if (competition.getOperatorId()!=null && competition.getOperatorId()!=0){
                        // 微信商户号
                        String s2 =storeClient.getmerchantNumberByOperatorId(competition.getOperatorId());
                        ResultUtil resultUtil = payMoneyUtil.fenzhangRefund(paymentCompetition.getFenzhangNo(), paymentCompetition.getFenzhangAmount(), s2, randomCode, randomCode1);
                        if (!resultUtil.getCode().equals(200)){
                            System.err.println("分账回退失败 原因是:"+resultUtil.getData().toString());
                        }else{
                            paymentCompetition.setFenzhangRefundNo(resultUtil.getData().toString());
                            paymentCompetitionMapper.updateById(paymentCompetition);
                        }
                    }
            if (store.getOperatorId()!=null && store.getOperatorId()!=0){
                String smidVx= storeClient.getmerchantNumberByOperatorId(store.getOperatorId());
                if (!StringUtils.hasLength(smidVx)){
                    System.err.println("运营商未配置微信商户号,获取支付失败!");
                }
            }
            Map<String, String> map = payMoneyUtil.wxRefund(payOrderNo, code, amount.toString(), amount.toString(),
                    "/base/competition/weChatCancelPaymentCompetitionCallback");
            String return_code = map.get("return_code");
            if (!"SUCCESS".equals(return_code)) {
                return ResultUtil.error(map.get("return_msg"));
            }
            String refund_id = map.get("refund_id");
            paymentCompetition.setRefundOrderNo(refund_id);
            paymentCompetition.setAppUserId(null);
            this.updateById(paymentCompetition);
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
                String codeRefund = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);// 退款单号
                // 运营商退款
                payMoneyUtil.weixinRefundV3(smidVx, codeRefund, paymentCompetition.getPayOrderNo(), paymentCompetition.getAmount().toString(), "/base/competition/weChatCancelPaymentCompetitionCallback1");
            }else {
                Map<String, String> map = payMoneyUtil.wxRefund(payOrderNo, code, amount.toString(), amount.toString(),
                        "/base/competition/weChatCancelPaymentCompetitionCallback");
                String return_code = map.get("return_code");
                if (!"SUCCESS".equals(return_code)) {
                    return ResultUtil.error(map.get("return_msg"));
                }
                String refund_id = map.get("refund_id");
                paymentCompetition.setRefundOrderNo(refund_id);
                paymentCompetition.setAppUserId(null);
                this.updateById(paymentCompetition);
            storeClient.addBackRecord(paymentCompetition.getAmount() + "_" + paymentCompetition.getAppUserId());
                storeClient.addBackRecord(paymentCompetition.getAmount() + "_" + paymentCompetition.getAppUserId());
            }
        }
        if (paymentCompetition.getPayType() == 2) {//支付宝支付
            Map<String, String> map = payMoneyUtil.aliRefund(payOrderNo, amount.toString());
cloud-server-competition/src/main/java/com/dsh/competition/util/PayMoneyUtil.java
@@ -8,10 +8,7 @@
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.dsh.competition.util.httpClinet.HttpClientUtil;
import com.dsh.competition.util.wx.PartnerAppPrepay;
import com.dsh.competition.util.wx.WXPayUtility;
import com.dsh.competition.util.wx.WeChatV3SignUtil;
import com.dsh.competition.util.wx.WxV3PayConfig;
import com.dsh.competition.util.wx.*;
import org.apache.commons.collections.map.HashedMap;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.dom4j.Document;
@@ -848,6 +845,39 @@
            return map2;
        }
    }
    public ResultUtil weixinRefundV3(String subMchid,String outRefundNo, String transactionId, String totalFee, String notifyUrl) throws Exception {
        BigDecimal bigDecimal = new BigDecimal(totalFee);
        int i = bigDecimal.multiply(new BigDecimal("100")).intValue();
        // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/partner/4013080340
        Create client = new Create(
                "1681873607",                    // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
                "55714944F7A7E52526F708280B176DCC838F371A",         // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
                "/usr/playpai/server/wxV3/1681873607_20250424_cert/apiclient_key.pem",    // 商户API证书私钥文件路径,本地文件路径
                "PUB_KEY_ID_0116818736072025042400351694002605",      // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
                "/usr/playpai/server/wxV3/pub_key.pem"        // 微信支付公钥文件路径,本地文件路径
        );
        Create.CreateRequest request = new Create.CreateRequest();
        request.subMchid = subMchid;
        request.transactionId = transactionId;
        request.outRefundNo = outRefundNo;
        request.reason = "退款";
        request.notifyUrl = callbackPath+notifyUrl;
        request.amount = new Create.AmountReq();
        request.amount.refund = (long) i;
        request.amount.total = (long) i;
        request.amount.currency = "CNY";
        try {
            Create.Refund response = client.run(request);
            // TODO: 请求成功,继续业务逻辑
            System.out.println(response);
        } catch (WXPayUtility.ApiException e) {
            // TODO: 请求失败,根据状态码执行不同的逻辑
            e.printStackTrace();
        }
        return ResultUtil.success();
    }
    /**
     * 发起分账回退
     * @param order 微信订单号
cloud-server-competition/src/main/java/com/dsh/competition/util/wx/Create.java
New file
@@ -0,0 +1,292 @@
package com.dsh.competition.util.wx;
import com.google.gson.annotations.SerializedName;
import okhttp3.*;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.List;
/**
 * 退款申请
 */
public class Create {
  private static String HOST = "https://api.mch.weixin.qq.com";
  private static String METHOD = "POST";
  private static String PATH = "/v3/refund/domestic/refunds";
  public Refund run(CreateRequest request) {
    String uri = PATH;
    String reqBody = WXPayUtility.toJson(request);
    Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
    reqBuilder.addHeader("Accept", "application/json");
    reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
    reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo, privateKey, METHOD, uri, reqBody));
    reqBuilder.addHeader("Content-Type", "application/json");
    RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
    reqBuilder.method(METHOD, requestBody);
    Request httpRequest = reqBuilder.build();
    // 发送HTTP请求
    OkHttpClient client = new OkHttpClient.Builder().build();
    try (Response httpResponse = client.newCall(httpRequest).execute()) {
      String respBody = WXPayUtility.extractBody(httpResponse);
      if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
        // 2XX 成功,验证应答签名
        WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
                                      httpResponse.headers(), respBody);
        // 从HTTP应答报文构建返回数据
        return WXPayUtility.fromJson(respBody, Refund.class);
      } else {
        throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
      }
    } catch (IOException e) {
      throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
    }
  }
  private final String mchid;
  private final String certificateSerialNo;
  private final PrivateKey privateKey;
  private final String wechatPayPublicKeyId;
  private final PublicKey wechatPayPublicKey;
  public Create(String mchid, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) {
    this.mchid = mchid;
    this.certificateSerialNo = certificateSerialNo;
    this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyFilePath);
    this.wechatPayPublicKeyId = wechatPayPublicKeyId;
    this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath);
  }
  public enum Status {
    @SerializedName("SUCCESS")
    SUCCESS,
    @SerializedName("CLOSED")
    CLOSED,
    @SerializedName("PROCESSING")
    PROCESSING,
    @SerializedName("ABNORMAL")
    ABNORMAL
  }
  public enum Account {
    @SerializedName("AVAILABLE")
    AVAILABLE,
    @SerializedName("UNAVAILABLE")
    UNAVAILABLE
  }
  public enum PromotionType {
    @SerializedName("COUPON")
    COUPON,
    @SerializedName("DISCOUNT")
    DISCOUNT
  }
  public static class GoodsDetail {
    @SerializedName("merchant_goods_id")
    public String merchantGoodsId;
    @SerializedName("wechatpay_goods_id")
    public String wechatpayGoodsId;
    @SerializedName("goods_name")
    public String goodsName;
    @SerializedName("unit_price")
    public Long unitPrice;
    @SerializedName("refund_amount")
    public Long refundAmount;
    @SerializedName("refund_quantity")
    public Integer refundQuantity;
  }
  public static class CreateRequest {
    @SerializedName("sub_mchid")
    public String subMchid;
    @SerializedName("transaction_id")
    public String transactionId;
    @SerializedName("out_trade_no")
    public String outTradeNo;
    @SerializedName("out_refund_no")
    public String outRefundNo;
    @SerializedName("reason")
    public String reason;
    @SerializedName("notify_url")
    public String notifyUrl;
    @SerializedName("funds_account")
    public ReqFundsAccount fundsAccount;
    @SerializedName("amount")
    public AmountReq amount;
    @SerializedName("goods_detail")
    public List<GoodsDetail> goodsDetail;
  }
  public enum Channel {
    @SerializedName("ORIGINAL")
    ORIGINAL,
    @SerializedName("BALANCE")
    BALANCE,
    @SerializedName("OTHER_BALANCE")
    OTHER_BALANCE,
    @SerializedName("OTHER_BANKCARD")
    OTHER_BANKCARD
  }
  public static class Amount {
    @SerializedName("total")
    public Long total;
    @SerializedName("refund")
    public Long refund;
    @SerializedName("from")
    public List<FundsFromItem> from;
    @SerializedName("payer_total")
    public Long payerTotal;
    @SerializedName("payer_refund")
    public Long payerRefund;
    @SerializedName("settlement_refund")
    public Long settlementRefund;
    @SerializedName("settlement_total")
    public Long settlementTotal;
    @SerializedName("discount_refund")
    public Long discountRefund;
    @SerializedName("currency")
    public String currency;
    @SerializedName("refund_fee")
    public Long refundFee;
  }
  public enum ReqFundsAccount {
    @SerializedName("AVAILABLE")
    AVAILABLE,
    @SerializedName("UNSETTLED")
    UNSETTLED
  }
  public enum FundsAccount {
    @SerializedName("UNSETTLED")
    UNSETTLED,
    @SerializedName("AVAILABLE")
    AVAILABLE,
    @SerializedName("UNAVAILABLE")
    UNAVAILABLE,
    @SerializedName("OPERATION")
    OPERATION,
    @SerializedName("BASIC")
    BASIC,
    @SerializedName("ECNY_BASIC")
    ECNY_BASIC
  }
  public static class Promotion {
    @SerializedName("promotion_id")
    public String promotionId;
    @SerializedName("scope")
    public PromotionScope scope;
    @SerializedName("type")
    public PromotionType type;
    @SerializedName("amount")
    public Long amount;
    @SerializedName("refund_amount")
    public Long refundAmount;
    @SerializedName("goods_detail")
    public List<GoodsDetail> goodsDetail;
  }
  public static class Refund {
    @SerializedName("refund_id")
    public String refundId;
    @SerializedName("out_refund_no")
    public String outRefundNo;
    @SerializedName("transaction_id")
    public String transactionId;
    @SerializedName("out_trade_no")
    public String outTradeNo;
    @SerializedName("channel")
    public Channel channel;
    @SerializedName("user_received_account")
    public String userReceivedAccount;
    @SerializedName("success_time")
    public String successTime;
    @SerializedName("create_time")
    public String createTime;
    @SerializedName("status")
    public Status status;
    @SerializedName("funds_account")
    public FundsAccount fundsAccount;
    @SerializedName("amount")
    public Amount amount;
    @SerializedName("promotion_detail")
    public List<Promotion> promotionDetail;
  }
  public static class FundsFromItem {
    @SerializedName("account")
    public Account account;
    @SerializedName("amount")
    public Long amount;
  }
  public static class AmountReq {
    @SerializedName("refund")
    public Long refund;
    @SerializedName("from")
    public List<FundsFromItem> from;
    @SerializedName("total")
    public Long total;
    @SerializedName("currency")
    public String currency;
  }
  public enum PromotionScope {
    @SerializedName("GLOBAL")
    GLOBAL,
    @SerializedName("SINGLE")
    SINGLE
  }
}
cloud-server-other/src/main/java/com/dsh/other/controller/SiteController.java
@@ -709,6 +709,7 @@
    })
    public ResultUtil cancelMySite(Integer id) {
        try {
            System.err.println("预约数据id");
            Integer uid = tokenUtil.getUserIdFormRedis();
            if (null == uid) {
                return ResultUtil.tokenErr();
@@ -756,7 +757,59 @@
            e.printStackTrace();
        }
    }
    /**
     * 微信退款回调V3服务商版本
     *
     * @param request
     * @param response
     */
    @ResponseBody
    @PostMapping("/base/site/cancelMySiteCallback1")
    public void cancelMySiteCallback1(HttpServletRequest request, HttpServletResponse response) {
        try {
            System.err.println("微信回调");
            System.err.println("请求" + request);
            BufferedReader reader = request.getReader();
            String string1 = reader.toString();
            System.err.println("请求reader" + string1);
            StringBuilder requestBody = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                requestBody.append(line);
            }
            System.err.println("全部请求体" + requestBody);
            JSONObject jsonObject = JSONObject.parseObject(requestBody.toString());
            JSONObject resource = jsonObject.getJSONObject("resource");
            AesUtil aesUtil = new AesUtil(WxV3PayConfig.apiV3Key.getBytes(StandardCharsets.UTF_8));
            String decryptedData = aesUtil.decryptToString(resource.getString("associated_data").getBytes(StandardCharsets.UTF_8), resource.getString("nonce").getBytes(StandardCharsets.UTF_8),
                    resource.getString("ciphertext"));
            System.err.println("微信解密的字符串信息" + decryptedData);
            JSONObject jsonInfo = (JSONObject) JSONObject.parse(decryptedData);
            String code = jsonInfo.getString("out_trade_no");
            String transaction_id = jsonInfo.getString("transaction_id");
            String refund_status = jsonInfo.getString("refund_status");
            String out_refund_no = jsonInfo.getString("out_refund_no");
            if (refund_status.equals("SUCCESS")) {
                SiteBooking siteBooking = siteBookingService.getOne(new QueryWrapper<SiteBooking>().eq("orderNo", code).eq("state", 1));
                siteBooking.setStatus(5);
                siteBooking.setCancelTime(new Date());
                siteBooking.setRefundOrderNo(out_refund_no);
                siteBookingService.updateById(siteBooking);
                if (null != siteBooking.getUserCouponId()) {
                    UserCoupon userCoupon = userCouponClient.queryUserCouponById(new QueryUserCouponByIdAndUserId(siteBooking.getUserCouponId(), siteBooking.getAppUserId()));
                    userCoupon.setStatus(1);
                    userCouponClient.updateUserCoupon(userCoupon);
                }
                    PrintWriter out = response.getWriter();
                    out.write("SUCCESS");
                    out.flush();
                    out.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @ResponseBody
    @PostMapping("/api/site/continuePaymentMySite")
cloud-server-other/src/main/java/com/dsh/other/service/impl/SiteServiceImpl.java
@@ -65,6 +65,7 @@
    @Resource
    private UserCouponClient userCouponClient;
    @Resource
    private CouponClient couponClient;
@@ -708,43 +709,39 @@
        if (siteBooking.getStatus() != 1 && siteBooking.getStatus() != 2) {
            return ResultUtil.error("不能进行取消操作");
        }
        Integer storeId = siteBooking.getStoreId();
        Store store = storeService.getById(storeId);
        if (siteBooking.getPayType() == 1) {//微信支付
            if (StringUtils.hasLength(siteBooking.getFenzhangNo())){
                // 是分账订单 如果分账金额不为0 那么回退分账金额
                if (siteBooking.getFenzhangAmount()!=null && siteBooking.getFenzhangAmount().compareTo(BigDecimal.ZERO)>0){
                    String randomCode = UUIDUtil.getRandomCode(16);
                    String randomCode1 = UUIDUtil.getRandomCode(16);
                    if (byId.getOperatorId()!=null && byId.getOperatorId()!=0){
                        // 微信商户号
                        String s2 =siteService.getmerchantNumberByOperatorId(byId.getOperatorId());
                        ResultUtil resultUtil = payMoneyUtil.fenzhangRefund(siteBooking.getFenzhangNo(), siteBooking.getFenzhangAmount().multiply(new BigDecimal("100")), s2, randomCode, randomCode1);
                        if (!resultUtil.getCode().equals(200)){
                            System.err.println("分账回退失败 原因是:"+resultUtil.getMsg()+resultUtil.getData());
                        }else{
                            siteBooking.setFenzhangRefundNo(resultUtil.getMsg());
                            siteBookingService.updateById(siteBooking);
                        }
                    }
            if (store.getOperatorId()!=null && store.getOperatorId()!=0){
                String smidVx= operatorUserService.getmerchantNumberByOperatorId(store.getOperatorId());
                if (!StringUtils.hasLength(smidVx)){
                    return ResultUtil.error("运营商未配置微信商户号,获取支付失败!");
                }
            }
            Map<String, String> map = payMoneyUtil.wxRefund(siteBooking.getPayOrderNo(), siteBooking.getOrderNo(), siteBooking.getPayMoney().toString(),
                    siteBooking.getPayMoney().toString(), "/base/site/cancelMySiteCallback");
            if (null == map) {
                return ResultUtil.error("取消退款异常");
            }
            String return_code = map.get("return_code");
            if (!"SUCCESS".equals(return_code)) {
                return ResultUtil.error(map.get("return_msg"));
            }
            siteBooking.setCancelUserId(uid);
            siteBookingService.updateById(siteBooking);
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
                String code = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);// 退款单号
                // 运营商退款
                payMoneyUtil.weixinRefundV3(smidVx, code, siteBooking.getPayOrderNo(), siteBooking.getPayMoney().toString(), "/base/site/cancelMySiteCallback1");
            }else{
                Map<String, String> map = payMoneyUtil.wxRefund(siteBooking.getPayOrderNo(), siteBooking.getOrderNo(), siteBooking.getPayMoney().toString(),
                        siteBooking.getPayMoney().toString(), "/base/site/cancelMySiteCallback");
                if (null == map) {
                    return ResultUtil.error("取消退款异常");
                }
                String return_code = map.get("return_code");
                if (!"SUCCESS".equals(return_code)) {
                    return ResultUtil.error(map.get("return_msg"));
                }
                siteBooking.setCancelUserId(uid);
                siteBookingService.updateById(siteBooking);
            TBackRecord tBackRecord = new TBackRecord();
            tBackRecord.setMoney(siteBooking.getPayMoney());
            tBackRecord.setUserId(siteBooking.getAppUserId());
            tBackRecord.setTime(new Date());
            backRecordService.save(tBackRecord);
                TBackRecord tBackRecord = new TBackRecord();
                tBackRecord.setMoney(siteBooking.getPayMoney());
                tBackRecord.setUserId(siteBooking.getAppUserId());
                tBackRecord.setTime(new Date());
                backRecordService.save(tBackRecord);
            }
        }
        if (siteBooking.getPayType() == 2) {//支付宝
cloud-server-other/src/main/java/com/dsh/other/util/PayMoneyUtil.java
@@ -5,15 +5,9 @@
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.*;
import com.alipay.api.domain.*;
import com.alipay.api.msg.AlipayMsgClient;
import com.alipay.api.msg.MsgHandler;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.dsh.other.util.wx.PartnerAppPrepay;
import com.dsh.other.util.wx.WXPayUtility;
import com.dsh.other.util.wx.WeChatV3SignUtil;
import com.dsh.other.util.wx.WxV3PayConfig;
import lombok.Synchronized;
import com.dsh.other.util.wx.*;
import org.apache.commons.collections.map.HashedMap;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.dom4j.Document;
@@ -23,9 +17,7 @@
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
@@ -575,6 +567,39 @@
        System.err.println(map3);
        return ResultUtil.success(map3);
    }
    public ResultUtil weixinRefundV3(String subMchid,String outRefundNo, String transactionId, String totalFee, String notifyUrl) throws Exception {
        BigDecimal bigDecimal = new BigDecimal(totalFee);
        int i = bigDecimal.multiply(new BigDecimal("100")).intValue();
        // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/partner/4013080340
        Create client = new Create(
                "1681873607",                    // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
                "55714944F7A7E52526F708280B176DCC838F371A",         // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
                "/usr/playpai/server/wxV3/1681873607_20250424_cert/apiclient_key.pem",    // 商户API证书私钥文件路径,本地文件路径
                "PUB_KEY_ID_0116818736072025042400351694002605",      // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
                "/usr/playpai/server/wxV3/pub_key.pem"        // 微信支付公钥文件路径,本地文件路径
        );
        Create.CreateRequest request = new Create.CreateRequest();
        request.subMchid = subMchid;
        request.transactionId = transactionId;
        request.outRefundNo = outRefundNo;
        request.reason = "退款";
        request.notifyUrl = callbackPath+notifyUrl;
        request.amount = new Create.AmountReq();
        request.amount.refund = (long) i;
        request.amount.total = (long) i;
        request.amount.currency = "CNY";
        try {
            Create.Refund response = client.run(request);
            // TODO: 请求成功,继续业务逻辑
            System.out.println(response);
        } catch (WXPayUtility.ApiException e) {
            // TODO: 请求失败,根据状态码执行不同的逻辑
            e.printStackTrace();
        }
        return ResultUtil.success();
    }
    /**
     * 发起分账
cloud-server-other/src/main/java/com/dsh/other/util/wx/Create.java
New file
@@ -0,0 +1,300 @@
package com.dsh.other.util.wx;
import com.dsh.other.util.wx.WXPayUtility;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
 * 退款申请
 */
public class Create {
  private static String HOST = "https://api.mch.weixin.qq.com";
  private static String METHOD = "POST";
  private static String PATH = "/v3/refund/domestic/refunds";
  public Refund run(CreateRequest request) {
    String uri = PATH;
    String reqBody = WXPayUtility.toJson(request);
    Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
    reqBuilder.addHeader("Accept", "application/json");
    reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
    reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo, privateKey, METHOD, uri, reqBody));
    reqBuilder.addHeader("Content-Type", "application/json");
    RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
    reqBuilder.method(METHOD, requestBody);
    Request httpRequest = reqBuilder.build();
    // 发送HTTP请求
    OkHttpClient client = new OkHttpClient.Builder().build();
    try (Response httpResponse = client.newCall(httpRequest).execute()) {
      String respBody = WXPayUtility.extractBody(httpResponse);
      if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
        // 2XX 成功,验证应答签名
        WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
                                      httpResponse.headers(), respBody);
        // 从HTTP应答报文构建返回数据
        return WXPayUtility.fromJson(respBody, Refund.class);
      } else {
        throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
      }
    } catch (IOException e) {
      throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
    }
  }
  private final String mchid;
  private final String certificateSerialNo;
  private final PrivateKey privateKey;
  private final String wechatPayPublicKeyId;
  private final PublicKey wechatPayPublicKey;
  public Create(String mchid, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) {
    this.mchid = mchid;
    this.certificateSerialNo = certificateSerialNo;
    this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyFilePath);
    this.wechatPayPublicKeyId = wechatPayPublicKeyId;
    this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath);
  }
  public enum Status {
    @SerializedName("SUCCESS")
    SUCCESS,
    @SerializedName("CLOSED")
    CLOSED,
    @SerializedName("PROCESSING")
    PROCESSING,
    @SerializedName("ABNORMAL")
    ABNORMAL
  }
  public enum Account {
    @SerializedName("AVAILABLE")
    AVAILABLE,
    @SerializedName("UNAVAILABLE")
    UNAVAILABLE
  }
  public enum PromotionType {
    @SerializedName("COUPON")
    COUPON,
    @SerializedName("DISCOUNT")
    DISCOUNT
  }
  public static class GoodsDetail {
    @SerializedName("merchant_goods_id")
    public String merchantGoodsId;
    @SerializedName("wechatpay_goods_id")
    public String wechatpayGoodsId;
    @SerializedName("goods_name")
    public String goodsName;
    @SerializedName("unit_price")
    public Long unitPrice;
    @SerializedName("refund_amount")
    public Long refundAmount;
    @SerializedName("refund_quantity")
    public Integer refundQuantity;
  }
  public static class CreateRequest {
    @SerializedName("sub_mchid")
    public String subMchid;
    @SerializedName("transaction_id")
    public String transactionId;
    @SerializedName("out_trade_no")
    public String outTradeNo;
    @SerializedName("out_refund_no")
    public String outRefundNo;
    @SerializedName("reason")
    public String reason;
    @SerializedName("notify_url")
    public String notifyUrl;
    @SerializedName("funds_account")
    public ReqFundsAccount fundsAccount;
    @SerializedName("amount")
    public AmountReq amount;
    @SerializedName("goods_detail")
    public List<GoodsDetail> goodsDetail;
  }
  public enum Channel {
    @SerializedName("ORIGINAL")
    ORIGINAL,
    @SerializedName("BALANCE")
    BALANCE,
    @SerializedName("OTHER_BALANCE")
    OTHER_BALANCE,
    @SerializedName("OTHER_BANKCARD")
    OTHER_BANKCARD
  }
  public static class Amount {
    @SerializedName("total")
    public Long total;
    @SerializedName("refund")
    public Long refund;
    @SerializedName("from")
    public List<FundsFromItem> from;
    @SerializedName("payer_total")
    public Long payerTotal;
    @SerializedName("payer_refund")
    public Long payerRefund;
    @SerializedName("settlement_refund")
    public Long settlementRefund;
    @SerializedName("settlement_total")
    public Long settlementTotal;
    @SerializedName("discount_refund")
    public Long discountRefund;
    @SerializedName("currency")
    public String currency;
    @SerializedName("refund_fee")
    public Long refundFee;
  }
  public enum ReqFundsAccount {
    @SerializedName("AVAILABLE")
    AVAILABLE,
    @SerializedName("UNSETTLED")
    UNSETTLED
  }
  public enum FundsAccount {
    @SerializedName("UNSETTLED")
    UNSETTLED,
    @SerializedName("AVAILABLE")
    AVAILABLE,
    @SerializedName("UNAVAILABLE")
    UNAVAILABLE,
    @SerializedName("OPERATION")
    OPERATION,
    @SerializedName("BASIC")
    BASIC,
    @SerializedName("ECNY_BASIC")
    ECNY_BASIC
  }
  public static class Promotion {
    @SerializedName("promotion_id")
    public String promotionId;
    @SerializedName("scope")
    public PromotionScope scope;
    @SerializedName("type")
    public PromotionType type;
    @SerializedName("amount")
    public Long amount;
    @SerializedName("refund_amount")
    public Long refundAmount;
    @SerializedName("goods_detail")
    public List<GoodsDetail> goodsDetail;
  }
  public static class Refund {
    @SerializedName("refund_id")
    public String refundId;
    @SerializedName("out_refund_no")
    public String outRefundNo;
    @SerializedName("transaction_id")
    public String transactionId;
    @SerializedName("out_trade_no")
    public String outTradeNo;
    @SerializedName("channel")
    public Channel channel;
    @SerializedName("user_received_account")
    public String userReceivedAccount;
    @SerializedName("success_time")
    public String successTime;
    @SerializedName("create_time")
    public String createTime;
    @SerializedName("status")
    public Status status;
    @SerializedName("funds_account")
    public FundsAccount fundsAccount;
    @SerializedName("amount")
    public Amount amount;
    @SerializedName("promotion_detail")
    public List<Promotion> promotionDetail;
  }
  public static class FundsFromItem {
    @SerializedName("account")
    public Account account;
    @SerializedName("amount")
    public Long amount;
  }
  public static class AmountReq {
    @SerializedName("refund")
    public Long refund;
    @SerializedName("from")
    public List<FundsFromItem> from;
    @SerializedName("total")
    public Long total;
    @SerializedName("currency")
    public String currency;
  }
  public enum PromotionScope {
    @SerializedName("GLOBAL")
    GLOBAL,
    @SerializedName("SINGLE")
    SINGLE
  }
}