From e31a75558bd7b8ac200194d11d181d0c6e319aa9 Mon Sep 17 00:00:00 2001 From: puzhibing <393733352@qq.com> Date: 星期五, 13 十月 2023 11:44:21 +0800 Subject: [PATCH] 对接第三方分账接口 --- ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/shop/ShopService.java | 12 ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/domain/dto/MgtShopHFTXAuthDto.java | 141 ++++++++ ruoyi-modules/ruoyi-shop/pom.xml | 6 ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/controller/management/MgtShopController.java | 17 + ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/domain/vo/MerchantBasicdataVo.java | 35 ++ ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/impl/shop/ShopServiceImpl.java | 10 ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/order/OrderServiceImpl.java | 50 ++ ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/HuiFuTianXiaUtil.java | 718 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 988 insertions(+), 1 deletions(-) diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/order/OrderServiceImpl.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/order/OrderServiceImpl.java index 8843be5..383a92c 100644 --- a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/order/OrderServiceImpl.java +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/order/OrderServiceImpl.java @@ -906,6 +906,56 @@ } + + + /** + * 小程序支付下单API + * @param appPlaceOrderVo + * @param userId + * @param shopId + * @param goodsName + * @param outTradeNo + * @param orderId + * @param payMoney + * @param openid + * @param payerClientIp + */ + + private void createWxPayInfo(AppPlaceOrderVo appPlaceOrderVo, Long userId, Long shopId, + String goodsName, String outTradeNo, + String orderId, BigDecimal payMoney, + String openid, String payerClientIp){ + try { + + + + // 支付相关信息返回 + appPlaceOrderVo.setAppId(result.getAppId()); + appPlaceOrderVo.setMchId(config.getMchId()); + appPlaceOrderVo.setTimeStamp(result.getTimeStamp()); + appPlaceOrderVo.setNonceStr(result.getNonceStr()); + appPlaceOrderVo.setPackageStr(result.getPackageValue()); + appPlaceOrderVo.setSignType(result.getSignType()); + appPlaceOrderVo.setPaySign(result.getPaySign()); + appPlaceOrderVo.setPrepayId(result.getPackageValue()); + + // 保存支付订单统一下单日志 + paymentMessageService.savePaymentMessage("1", orderId, payRequestJson, payResponseJson); + + // 保存支付订单统一下单支付记录 + orderPaymentService.saveOrderPayment(userId, shopId, mainMchId, orderId, outTradeNo, payMoney, + appPlaceOrderVo.getEndTime(), "Y", openid, + goodsName, result.getPackageValue()); + }catch (Exception e){ + e.printStackTrace(); + } + + } + + + + + /** * @param appPlaceActivityDto * @return AppPlaceOrderVo diff --git a/ruoyi-modules/ruoyi-shop/pom.xml b/ruoyi-modules/ruoyi-shop/pom.xml index cd02e93..c6c0011 100644 --- a/ruoyi-modules/ruoyi-shop/pom.xml +++ b/ruoyi-modules/ruoyi-shop/pom.xml @@ -166,7 +166,11 @@ <version>4.13.1</version> <scope>test</scope> </dependency> - + <dependency> + <groupId>cn.hutool</groupId> + <artifactId>hutool-all</artifactId> + <version>5.8.22</version> + </dependency> </dependencies> diff --git a/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/controller/management/MgtShopController.java b/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/controller/management/MgtShopController.java index 001e8a6..23bd684 100644 --- a/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/controller/management/MgtShopController.java +++ b/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/controller/management/MgtShopController.java @@ -281,6 +281,19 @@ return R.ok(); } + + @RequestMapping(value = "/mgtShopHFTXAuth", method = RequestMethod.POST) + @Log(title = "商户进件管理", businessType = BusinessType.UPDATE,operContent = "商户进件") + @ApiOperation(value = "平台商户进件") + public R mgtShopHFTXAuth(@Validated @RequestBody MgtShopHFTXAuthDto mgtShopAuthDto) { + Long userId = SecurityUtils.getUserId(); + mgtShopAuthDto.setUserId(userId); + shopService.mgtShopHFTXAuth(mgtShopAuthDto); + return R.ok(); + } + + + @RequestMapping(value = "/getMgtShopAuth", method = RequestMethod.POST) @ApiOperation(value = "获取商户进件信息") public R<MgtShopAuthGetVo> getMgtShopAuth(@RequestBody MgtShopAuthGetDto mgtShopAuthGetDto) { @@ -304,4 +317,8 @@ shopService.addProfitSharingReceiver(mgtShopAuthGetDto); return R.ok(); } + + + + } diff --git a/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/domain/dto/MgtShopHFTXAuthDto.java b/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/domain/dto/MgtShopHFTXAuthDto.java new file mode 100644 index 0000000..d8c1d91 --- /dev/null +++ b/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/domain/dto/MgtShopHFTXAuthDto.java @@ -0,0 +1,141 @@ +package com.ruoyi.shop.domain.dto; + +import com.ruoyi.system.api.domain.dto.MgtBaseDto; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + * @ClassName Mgt + * @Description TODO + * @Author jqs + * @Date 2023/6/19 10:57 + * @Version 1.0 + */ +@Data +public class MgtShopHFTXAuthDto extends MgtBaseDto { + @ApiModelProperty(value = "进件类型(1=用户,2=企业)", dataType = "int", required = true) + private Integer type; + @ApiModelProperty(value = "商户名称", dataType = "string", required = true) + private String reg_name; + @ApiModelProperty(value = "商户简称", dataType = "string", required = true) + private String short_name; + @ApiModelProperty(value = "公司类型(1:政府机构,2:国营企业,3:私营企业,4:外资企业,5:个体工商户,7:事业单位)", dataType = "string", required = true) + private String ent_type; + @ApiModelProperty(value = "营业执照编号", dataType = "string", required = true) + private String license_code; + @ApiModelProperty(value = "营业执照有效期类型(0:非长期有效 ,1:长期有效)", dataType = "string", required = true) + private String license_validity_type; + @ApiModelProperty(value = "营业执照有效期开始日期(日期格式:yyyyMMdd)", dataType = "string", required = true) + private String license_begin_date; + @ApiModelProperty(value = "营业执照有效期截止日期(日期格式:yyyyMMdd,当license_validity_type=0时必填 ;当license_validity_type=1时为空)", dataType = "string", required = true) + private String license_end_date; + @ApiModelProperty(value = "注册省(示例值:310000)", dataType = "string", required = true) + private String reg_prov_id; + @ApiModelProperty(value = "注册省(注册市:310000)", dataType = "string", required = true) + private String reg_area_id; + @ApiModelProperty(value = "注册区(示例值:310000)", dataType = "string", required = true) + private String reg_district_id; + @ApiModelProperty(value = "注册详细地址", dataType = "string", required = true) + private String reg_detail; + @ApiModelProperty(value = "法人姓名", dataType = "string", required = true) + private String legal_name; + @ApiModelProperty(value = "法人证件类型", dataType = "string", required = true) + private String legal_cert_type; + @ApiModelProperty(value = "法人证件号码", dataType = "string", required = true) + private String legal_cert_no; + @ApiModelProperty(value = "法人证件有效期类型(1:长期有效;0:非长期有效 )", dataType = "string", required = true) + private String legal_cert_validity_type; + @ApiModelProperty(value = "法人证件有效期开始日期(日期格式:yyyyMMdd,以北京时间为准)", dataType = "string", required = true) + private String legal_cert_begin_date; + @ApiModelProperty(value = "法人证件有效期截止日期(日期格式:yyyyMMdd,当legal_cert_validity_type=0时必填 ;当legal_cert_validity_type=1时为空 )", dataType = "string", required = true) + private String legal_cert_end_date; + @ApiModelProperty(value = "经营省", dataType = "string", required = true) + private String prov_id; + @ApiModelProperty(value = "经营市", dataType = "string", required = true) + private String area_id; + @ApiModelProperty(value = "经营区", dataType = "string", required = true) + private String district_id; + @ApiModelProperty(value = "经营详细地址", dataType = "string", required = true) + private String detail_addr; + @ApiModelProperty(value = "联系人姓名", dataType = "string", required = true) + private String contact_name; + @ApiModelProperty(value = "联系人手机号", dataType = "string", required = true) + private String contact_mobile_no; + @ApiModelProperty(value = "联系人电子邮箱", dataType = "string", required = true) + private String contact_email; + @ApiModelProperty(value = "客服电话", dataType = "string", required = true) + private String service_phone; + @ApiModelProperty(value = "小票名称", dataType = "string", required = true) + private String receipt_name; + @ApiModelProperty(value = "结算账户类型(结算账户类型,个人商户/用户不支持填写对公账户和对私非法人账户\n" + + "0:对公账户 1:对私法人账户 2:对私非法人账户)", dataType = "string", required = true) + private String card_type; + @ApiModelProperty(value = "结算账户名(当card_type=0时填写企业名称\n" + + "当card_type=1时填写法人姓名,对私法人结算银行户名与法人姓名必需一致;\n" + + "当card_type=2时填写非法人姓名)", dataType = "string", required = true) + private String card_name; + @ApiModelProperty(value = "结算账号", dataType = "string", required = true) + private String card_no; + @ApiModelProperty(value = "银行所在省", dataType = "string", required = true) + private String js_prov_id; + @ApiModelProperty(value = "银行所在市", dataType = "string", required = true) + private String js_area_id; + @ApiModelProperty(value = "联行号", dataType = "string", required = true) + private String branch_code; + @ApiModelProperty(value = "持卡人证件类型(当card_type=0时为空, 当card_type=1或2时必填)", dataType = "string", required = true) + private String cert_type; + @ApiModelProperty(value = "持卡人证件号码(对私结算必填;年龄不小于18岁且不能大于80岁;如持卡人证件类型为00:身份证,则填写身份证号码 )", dataType = "string", required = true) + private String cert_no; + @ApiModelProperty(value = "持卡人证件类型(1:长期有效 0:非长期有效)", dataType = "string", required = true) + private String cert_validity_type; + @ApiModelProperty(value = "持卡人证件有效期开始(日期格式:yyyyMMdd,当cert_validity_type=0时必填;当cert_validity_type=1时为空)", dataType = "string", required = true) + private String cert_begin_date; + @ApiModelProperty(value = "持卡人证件有效期截止日期(日期格式:yyyyMMdd,当cert_validity_type=0时必填;当cert_validity_type=1时为空)", dataType = "string", required = true) + private String cert_end_date; + @ApiModelProperty(value = "银行卡绑定手机号(对私非法人结算必填,开通全域资金管理功能必填)", dataType = "string", required = true) + private String mp; + @ApiModelProperty(value = "商户ICP备案编号", dataType = "string", required = true) + private String mer_icp; + @ApiModelProperty(value = "基本存款账户编号或核准号", dataType = "string", required = true) + private String open_licence_no; + @ApiModelProperty(value = "开户许可证", dataType = "string", required = true) + private String reg_acct_pic; + @ApiModelProperty(value = "结算卡正面", dataType = "string", required = true) + private String settle_card_front_pic; + @ApiModelProperty(value = "结算人身份证国徽面", dataType = "string", required = true) + private String settle_cert_back_pic; + @ApiModelProperty(value = "结算人身份证人像面", dataType = "string", required = true) + private String settle_cert_front_pic; + @ApiModelProperty(value = "税务登记证", dataType = "string", required = true) + private String tax_reg_pic; + @ApiModelProperty(value = "法人身份证国徽面", dataType = "string", required = true) + private String legal_cert_back_pic; + @ApiModelProperty(value = "法人身份证人像面", dataType = "string", required = true) + private String legal_cert_front_pic; + @ApiModelProperty(value = "营业执照图片", dataType = "string", required = true) + private String license_pic; + @ApiModelProperty(value = "授权委托书", dataType = "string", required = true) + private String auth_enturst_pic; + @ApiModelProperty(value = "注册资本", dataType = "string", required = true) + private String reg_capital; + @ApiModelProperty(value = "经营范围", dataType = "string", required = true) + private String business_scope; + @ApiModelProperty(value = "成立时间,示例值:20091212", dataType = "string", required = true) + private String found_date; + @ApiModelProperty(value = "线下经营-门头照", dataType = "string", required = true) + private String store_header_pic; + @ApiModelProperty(value = "线下经营-内景照", dataType = "string", required = true) + private String store_indoor_pic; + @ApiModelProperty(value = "线下经营-收银台", dataType = "string", required = true) + private String store_cashier_desk_pic; + @ApiModelProperty(value = "线上经营-公司前台", dataType = "string", required = true) + private String online_company_pic; + @ApiModelProperty(value = "线上经营-工作区域", dataType = "string", required = true) + private String online_company_area_pic; + @ApiModelProperty(value = "法人手机号", dataType = "string", required = true) + private String legal_mobile_no; + @ApiModelProperty(value = "法人身份证地址", dataType = "string", required = true) + private String legal_addr; +} diff --git a/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/domain/vo/MerchantBasicdataVo.java b/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/domain/vo/MerchantBasicdataVo.java new file mode 100644 index 0000000..8a21074 --- /dev/null +++ b/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/domain/vo/MerchantBasicdataVo.java @@ -0,0 +1,35 @@ +package com.ruoyi.shop.domain.vo; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2023/10/12 15:41 + */ +@Data +public class MerchantBasicdataVo { + /** + * 状态(1=处理中,2=审核中,3=成功) + */ + private Integer status; + /** + * 审核结果;Y:审核通过,N:审核拒绝,F:失败 + */ + private String audit_status; + /** + * 审核描述,审核人员编辑的审核描述 + */ + private String audit_desc; + /** + * 商户号 + */ + private String huifu_id; + /** + * 取现卡序列号 + */ + private String token_no; + /** + * 申请单号 + */ + private String apply_no; +} diff --git a/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/impl/shop/ShopServiceImpl.java b/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/impl/shop/ShopServiceImpl.java index d2f60f6..4e7c994 100644 --- a/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/impl/shop/ShopServiceImpl.java +++ b/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/impl/shop/ShopServiceImpl.java @@ -1063,6 +1063,16 @@ shopAuthenticationService.saveOrUpdate(shopAuthentication); } + + /** + * 商户进件(汇付天下) + * @param mgtShopHFTXAuthDto + */ + @Override + public void mgtShopHFTXAuth(MgtShopHFTXAuthDto mgtShopHFTXAuthDto) { + + } + /** * @description 获取平台商户统计 * @author jqs diff --git a/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/shop/ShopService.java b/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/shop/ShopService.java index 75f51f2..d40562c 100644 --- a/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/shop/ShopService.java +++ b/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/service/shop/ShopService.java @@ -208,6 +208,18 @@ */ void mgtShopAuth(MgtShopAuthDto mgtShopAuthDto); + + /** + * @description 商户进件 + * @author jqs + * @date 2023/6/19 11:02 + * @param mgtShopHFTXAuthDto + * @return void + */ + void mgtShopHFTXAuth(MgtShopHFTXAuthDto mgtShopHFTXAuthDto); + + + /** * @description 获取平台商户统计 * @author jqs diff --git a/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/HuiFuTianXiaUtil.java b/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/HuiFuTianXiaUtil.java new file mode 100644 index 0000000..eb1c6dd --- /dev/null +++ b/ruoyi-modules/ruoyi-shop/src/main/java/com/ruoyi/shop/util/HuiFuTianXiaUtil.java @@ -0,0 +1,718 @@ +package com.ruoyi.shop.util; + +import cn.hutool.http.*; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.shop.domain.dto.MgtShopHFTXAuthDto; +import com.ruoyi.shop.domain.vo.MerchantBasicdataVo; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.springframework.util.StringUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * 汇付天下工具类 + * @author zhibing.pu + * @Date 2023/10/12 0:02 + */ +public class HuiFuTianXiaUtil { + //私钥(Base64编码) + private static String privateKeyBase64 = ""; + //公钥(Base64编码) + private static String publicKeyBase64 = ""; + //商户号 + private static String huifu_id = "6666000140729384"; + //产品号 + private static String product_id = ""; + //微信小程序appid + private static String sub_appid = ""; + //本地文件存储位置 + private static String path = ""; + //控制台地址https://dashboard.huifu.com/partners/login,用户名为hongrt,最新密码为hongrt_2023@ + + + + /** + * 微信小程序支付 + * @param req_seq_id 支付流水号 + * @param goods_desc 商品描述 + * @param trans_amt 支付金额 + * @param sub_openid 微信小程序用户openid + * @param notify_url 异步通知回调地址 + * @param acct_split_bunch 分账对象[{"div_amt":"0.10","huifu_id":"6666000123120001"}] + */ + public static R<> weixinPayment(String req_seq_id, String goods_desc, Double trans_amt, String sub_openid, String notify_url, JSONArray acct_split_bunch){ + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); + NumberFormat numberInstance = NumberFormat.getInstance(); + //最大两位小数 + numberInstance.setMaximumFractionDigits(2); + String url = "https://api.huifu.com/v2/trade/payment/jspay"; + JSONObject data = new JSONObject(); + data.put("req_date", sdf.format(new Date())); + data.put("req_seq_id", req_seq_id); + data.put("huifu_id", huifu_id); + data.put("goods_desc", goods_desc); + data.put("trade_type", "T_MINIAPP"); + data.put("trans_amt", numberInstance.format(trans_amt)); + + JSONObject wx_data = new JSONObject(); + wx_data.put("sub_appid", sub_appid); + wx_data.put("sub_openid", sub_openid); + data.put("wx_data", wx_data.toJSONString()); + data.put("notify_url", notify_url); + data.put("acct_split_bunch", acct_split_bunch.toJSONString()); + + JSONObject body = new JSONObject(); + body.put("sys_id", huifu_id); + body.put("product_id", product_id); + body.put("sign", sign(data.toJSONString())); + body.put("data", data); + HttpRequest post = HttpUtil.createPost(url); + Map<String, String> headers = new HashMap<>(); + headers.put("Content-type","application/json; charset=utf-8"); + headers.put("Accept", "application/json"); + post.addHeaders(headers); + post.body(body.toJSONString()); + HttpResponse execute = post.execute(); + String result1 = execute.body(); + execute.close(); + if(StringUtils.hasLength(result1)){ + JSONObject result = JSONObject.parseObject(result1); + String result_data = result.getString("data"); + String result_sign = result.getString("sign"); + boolean verify = verify(result_data, result_sign); + if(!verify){ + return R.fail("结果验签失败"); + } + JSONObject resultData = result.getJSONObject("data"); + /** + * 00000000 交易受理成功;注:交易状态以trans_stat为准; + * 00000100 下单成功 + * 10000000 产品号不能为空 + * 10000000 交易类型不能为空 + * 10000000 %s不能为空(%s代指报错参数名) + * 10000000 %s长度固定%d位(%s代指报错参数名、%d代指字段长度) + * 10000000 %s最大长度为%d位(%s代指报错参数名、%d代指字段长度) + * 10000000 %s的传入枚举[%s]不存在(%s代指报错参数名) + * 10000000 %s不符合%s格式(%s代指报错参数名)。如:交易金额不符合金额格式 + * 10000000 订单已超时 + * 20000000 重复交易 + * 21000000 手续费金额、手续费收取方式、手续费扣款标识、手续费子客户号、手续费账户号,必须同时为空或同时必填 + * 22000000 产品号不存在 + * 22000000 产品号状态异常 + * 22000002 商户信息不存在 + * 22000002 商户状态异常 + * 22000003 延迟账户不存在 + * 22000003 商户账户信息不存在 + * 22000004 暂未开通分账权限 + * 22000004 暂未开通%s权限(%s代指报错参数名) + * 22000004 暂未开通延迟入账权限 + * 22000005 手续费承担方必须参与分账 + * 22000005 分账列表必须包含主交易账户 + * 22000005 其他商户分账比例过高 + * 22000005 商户入驻信息配置有误(多通道) + * 22000005 商户分期贴息未激活 + * 22000005 分期交易不能重复激活 + * 22000005 手续费配置有误 + * 22000005 商户贴息信息未配置 + * 22000005 花呗分期费率配置有误 + * 22000005 分账配置有误 + * 22000005 分账配置未包含手续费承担方 + * 22000005 商户入驻配置信息有误 + * 22000005 商户支付宝/微信入驻信息配置有误 + * 22000005 商户银联入驻信息配置有误 + * 22000005 商户贴息分期费率未配置渠道号 + * 22000005 商户贴息分期费率未配置费率类型 + * 22000005 商户贴息分期费率配置有误 + * 22000005 手续费费率未配置 + * 22000005 手续费计算错误 + * 22000005 商户贴息信息配置有误 + * 22000005 商户未报名活动或活动已过期 + * 22000005 数字货币手续费费率未配置 + * 22000005 数字货币手续费配置有误 + * 22000005 商户未配置默认入驻信息(多通道) + * 23000003 交易金额不足以支付内扣手续费 + * 23000003 优惠金额大于交易金额 + * 23000004 交易类型不支持 + * 23000004 当前交易类型不支持商户贴息 + * 90000000 业务执行失败;如:账户可用余额不足 + * 90000000 该功能已关闭,请联系客服 + * 90000000 交易失败,单日金额超限,请联系额服提额 + * 90000000 交易存在风险 + * 91111119 通道异常,请稍后重试 + * 98888888 系统错误 + */ + String resp_code = resultData.getString("resp_code"); + String resp_desc = resultData.getString("resp_desc"); + //交易状态 P: 处理中、S: 成功、F: 失败 + String trans_stat = resultData.getString("trans_stat"); + //交易受理成功;注:交易状态以trans_stat为准; + String success1 = "00000000"; + //下单成功 + String success2 = "00000100"; + if(success1.equals(resp_code) || success2.equals(resp_code)){ + + } + return R.fail(resp_desc); + } + return R.fail("请求异常"); + } + + + + /** + * RSA私钥签名:签名方式SHA256WithRSA + * @param data 待签名字符串 + * @return 签名byte[] + * @throws Exception + */ + public static String sign(String data) { + //先对该json对象数据按照参数字典顺序(参数名ASCII码从小到大排序,参数名区分大小写)排序生成字符串,再进行加签和验签。 + data = JSON.toJSONString(JSONObject.parseObject(data, TreeMap.class)); + // Base64 --> Key + try { + byte[] bytes = Base64.getDecoder().decode(privateKeyBase64); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes); + KeyFactory keyFactory; + keyFactory = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = keyFactory.generatePrivate(keySpec); + // Sign + Signature signature = Signature.getInstance("SHA256WithRSA"); + signature.initSign(privateKey); + signature.update(data.getBytes("UTF-8")); + return Base64.getEncoder().encodeToString(signature.sign()); + } catch (Exception e) { + return null; + } + } + + + + /** + * 使用汇付RSA公钥验签 + * @param data 待签名字符串 + * @return 验签结果 + * @throws Exception + */ + public static boolean verify(String data, String sign) { + //先对该json对象数据按照参数字典顺序(参数名ASCII码从小到大排序,参数名区分大小写)排序生成字符串,再进行加签和验签。 + data = JSON.toJSONString(JSONObject.parseObject(data, TreeMap.class)); + // Base64 --> Key + try { + byte[] bytes = Base64.getDecoder().decode(publicKeyBase64); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes); + KeyFactory keyFactory; + keyFactory = KeyFactory.getInstance("RSA"); + PublicKey publicKey = keyFactory.generatePublic(keySpec); + // verify + Signature signature = Signature.getInstance("SHA256WithRSA"); + signature.initVerify(publicKey); + signature.update(data.getBytes("UTF-8")); + return signature.verify(Base64.getDecoder().decode(sign)); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + } + + + /** + * 商户进价 + * @param dto + * @param req_seq_id 流水号 + * @param async_return_url 异步通知回调地址 + * @return + */ + public static R<MerchantBasicdataVo> merchantBasicdataEnt(MgtShopHFTXAuthDto dto, String req_seq_id, String async_return_url){ + String url = "https://api.huifu.com/v2/merchant/basicdata/ent"; + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); + JSONObject data = new JSONObject(); + data.put("req_seq_id", req_seq_id); + data.put("req_date", sdf.format(new Date())); + data.put("upper_huifu_id", huifu_id); + data.put("reg_name", dto.getReg_name()); + data.put("short_name", dto.getShort_name()); + data.put("ent_type", dto.getEnt_type()); + data.put("license_code", dto.getLicense_code()); + data.put("license_validity_type", dto.getLicense_validity_type()); + data.put("license_begin_date", dto.getLicense_begin_date()); + if("0".equals(dto.getLicense_validity_type())){ + data.put("license_end_date", dto.getLicense_end_date()); + } + data.put("reg_prov_id", dto.getReg_prov_id()); + data.put("reg_area_id", dto.getReg_area_id()); + data.put("reg_district_id", dto.getReg_district_id()); + data.put("reg_detail", dto.getReg_detail()); + data.put("legal_name", dto.getLegal_name()); + data.put("legal_cert_type", dto.getLegal_cert_type()); + data.put("legal_cert_no", dto.getLegal_cert_no()); + data.put("legal_cert_validity_type", dto.getLegal_cert_validity_type()); + data.put("legal_cert_begin_date", dto.getLegal_cert_begin_date()); + if("0".equals(dto.getLegal_cert_validity_type())){ + data.put("legal_cert_end_date", dto.getLegal_cert_end_date()); + } + data.put("prov_id", dto.getProv_id()); + data.put("area_id", dto.getArea_id()); + data.put("district_id", dto.getDistrict_id()); + data.put("detail_addr", dto.getDetail_addr()); + data.put("contact_name", dto.getContact_name()); + data.put("contact_mobile_no", dto.getContact_mobile_no()); + data.put("contact_email", dto.getContact_email()); + data.put("service_phone", dto.getService_phone()); + data.put("busi_type", "1"); + data.put("receipt_name", dto.getReceipt_name()); + data.put("mcc", "7297"); + + //结算卡信息配置 + JSONObject card_info = new JSONObject(); + card_info.put("card_type", dto.getCard_type()); + card_info.put("card_name", dto.getCard_name()); + card_info.put("card_no", dto.getCard_no()); + if(!"0".equals(dto.getCard_type())){ + card_info.put("prov_id", dto.getJs_prov_id()); + card_info.put("area_id", dto.getJs_area_id()); + card_info.put("cert_type", dto.getCert_type()); + } + if("0".equals(dto.getCard_type())){ + card_info.put("branch_code", dto.getBranch_code()); + } + card_info.put("cert_no", dto.getCert_no()); + card_info.put("cert_validity_type", dto.getCert_validity_type()); + if("0".equals(dto.getCert_validity_type())){ + card_info.put("cert_end_date", dto.getCert_end_date()); + } + card_info.put("mp", dto.getMp()); + data.put("card_info", card_info.toJSONString()); + + //取现信息配置 + JSONObject cash_config = new JSONObject(); + cash_config.put("cash_type", "T1"); + cash_config.put("fix_amt", "0.00"); + data.put("cash_config", cash_config.toJSONString()); + + //结算规则配置 + JSONObject settle_config = new JSONObject(); + cash_config.put("settle_cycle", "T1"); + cash_config.put("settle_pattern", "P0"); + cash_config.put("settle_batch_no", "0"); + cash_config.put("constant_amt", "0.00"); + data.put("settle_config", settle_config.toJSONString()); + + data.put("async_return_url", async_return_url); + data.put("mer_icp", dto.getMer_icp()); + data.put("open_licence_no", dto.getOpen_licence_no()); + data.put("head_huifu_id", huifu_id); + data.put("reg_acct_pic", dto.getReg_acct_pic()); + data.put("settle_card_front_pic", dto.getSettle_card_front_pic()); + data.put("settle_cert_back_pic", dto.getSettle_cert_back_pic()); + data.put("settle_cert_front_pic", dto.getSettle_cert_front_pic()); + data.put("tax_reg_pic", dto.getTax_reg_pic()); + data.put("legal_cert_back_pic", dto.getLegal_cert_back_pic()); + data.put("legal_cert_front_pic", dto.getLegal_cert_front_pic()); + data.put("license_pic", dto.getLicense_pic()); + data.put("auth_enturst_pic", dto.getAuth_enturst_pic()); + data.put("reg_capital", dto.getReg_capital()); + data.put("business_scope", dto.getBusiness_scope()); + data.put("found_date", dto.getFound_date()); + data.put("scene_type", "ALL"); + data.put("store_header_pic", dto.getStore_header_pic()); + data.put("store_indoor_pic", dto.getStore_indoor_pic()); + data.put("store_cashier_desk_pic", dto.getStore_cashier_desk_pic()); + data.put("online_company_pic", dto.getOnline_company_pic()); + data.put("online_company_area_pic", dto.getOnline_company_area_pic()); + data.put("legal_mobile_no", dto.getLegal_mobile_no()); + data.put("legal_addr", dto.getLegal_addr()); + + JSONObject body = new JSONObject(); + body.put("sys_id", huifu_id); + body.put("product_id", product_id); + body.put("sign", sign(data.toJSONString())); + body.put("data", data); + HttpRequest post = HttpUtil.createPost(url); + Map<String, String> headers = new HashMap<>(); + headers.put("Content-type","application/json; charset=utf-8"); + headers.put("Accept", "application/json"); + post.addHeaders(headers); + post.body(body.toJSONString()); + HttpResponse execute = post.execute(); + String result = execute.body(); + execute.close(); + if(!StringUtils.hasLength(result)){ + return R.fail("请求异常"); + } + JSONObject resultData = JSON.parseObject(result); + String resp_code = resultData.getString("resp_code"); + String resp_desc = resultData.getString("resp_desc"); + //处理成功 + String success1 = "00000000"; + //交易正在处理中 + String success2 = "00000100"; + //审核中 + String success3 = "90000000"; + MerchantBasicdataVo vo = new MerchantBasicdataVo(); + if(success1.equals(resp_code) || success2.equals(resp_code) || success3.equals(resp_code)){ + vo.setStatus(success1.equals(resp_code) ? 1 : success3.equals(resp_code) ? 2 : 3); + vo.setHuifu_id(resultData.getString("huifu_id")); + vo.setApply_no(resultData.getString("apply_no")); + vo.setToken_no(resultData.getString("token_no")); + return R.ok(vo); + } + return R.fail(resp_desc); + } + + + /** + * 企业进件通知回调处理 + * @param jsonObject + * @return + */ + public static R<MerchantBasicdataVo> merchantBasicdataEntNotify(JSONObject jsonObject){ + String ok = "200"; + String success = "10000"; + String resp_code = jsonObject.getString("resp_code"); + if(ok.equals(resp_code) || success.equals(resp_code)){ + JSONObject data = jsonObject.getJSONObject("data"); + String sub_resp_code = data.getString("sub_resp_code"); + String sub_resp_desc = data.getString("sub_resp_desc"); + //处理成功 + String success1 = "00000000"; + //交易正在处理中 + String success2 = "00000100"; + //审核中 + String success3 = "90000000"; + MerchantBasicdataVo vo = new MerchantBasicdataVo(); + if(success1.equals(sub_resp_code) || success2.equals(sub_resp_code) || success3.equals(sub_resp_code)){ + vo.setStatus(success1.equals(sub_resp_code) ? 1 : success3.equals(sub_resp_code) ? 2 : 3); + vo.setAudit_status(data.getString("audit_status")); + vo.setAudit_desc(data.getString("audit_desc")); + vo.setHuifu_id(data.getString("huifu_id")); + vo.setApply_no(data.getString("apply_no")); + vo.setToken_no(data.getString("token_no")); + return R.ok(vo); + } + return R.fail(sub_resp_desc); + } + return R.fail(jsonObject.getString("resp_desc")); + } + + + + /** + * 个人商户进件 + * @param dto + * @param req_seq_id 流水号 + * @param async_return_url 异步通知回调地址 + * @return + */ + public static R<MerchantBasicdataVo> merchantBasicdataIndv(MgtShopHFTXAuthDto dto, String req_seq_id, String async_return_url){ + String url = "https://api.huifu.com/v2/merchant/basicdata/indv"; + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); + JSONObject data = new JSONObject(); + data.put("req_seq_id", req_seq_id); + data.put("req_date", sdf.format(new Date())); + data.put("upper_huifu_id", huifu_id); + data.put("reg_name", dto.getReg_name()); + data.put("prov_id", dto.getProv_id()); + data.put("area_id", dto.getArea_id()); + data.put("district_id", dto.getDistrict_id()); + data.put("detail_addr", dto.getDetail_addr()); + data.put("contact_name", dto.getContact_name()); + data.put("contact_mobile_no", dto.getContact_mobile_no()); + data.put("contact_email", dto.getContact_email()); + + //结算卡信息配置 + JSONObject card_info = new JSONObject(); + card_info.put("card_name", dto.getCard_name()); + card_info.put("card_no", dto.getCard_no()); + card_info.put("prov_id", dto.getJs_prov_id()); + card_info.put("area_id", dto.getJs_area_id()); + card_info.put("cert_type", dto.getCert_type()); + card_info.put("cert_no", dto.getCert_no()); + card_info.put("cert_validity_type", dto.getCert_validity_type()); + card_info.put("cert_begin_date", dto.getCert_begin_date()); + card_info.put("cert_end_date", dto.getCert_end_date()); + card_info.put("mp", dto.getMp()); + data.put("card_info", card_info.toJSONString()); + + //取现信息配置 + JSONObject cash_config = new JSONObject(); + cash_config.put("cash_type", "T1"); + cash_config.put("fix_amt", "0.00"); + data.put("cash_config", cash_config.toJSONString()); + + //结算规则配置 + JSONObject settle_config = new JSONObject(); + cash_config.put("settle_cycle", "T1"); + cash_config.put("settle_pattern", "P0"); + cash_config.put("settle_batch_no", "0"); + cash_config.put("constant_amt", "0.00"); + data.put("settle_config", settle_config.toJSONString()); + + data.put("async_return_url", async_return_url); + data.put("mer_icp", dto.getMer_icp()); + data.put("scene_type", "ALL"); + data.put("legal_cert_no", dto.getLegal_cert_no()); + data.put("legal_cert_validity_type", dto.getLegal_cert_validity_type()); + data.put("legal_cert_begin_date", dto.getLegal_cert_begin_date()); + data.put("legal_cert_end_date", dto.getLegal_cert_end_date()); + data.put("legal_addr", dto.getLegal_addr()); + data.put("legal_cert_back_pic", dto.getLegal_cert_back_pic()); + data.put("legal_cert_front_pic", dto.getLegal_cert_front_pic()); + data.put("mcc", "7297"); + data.put("store_header_pic", dto.getStore_header_pic()); + data.put("store_indoor_pic", dto.getStore_indoor_pic()); + data.put("store_cashier_desk_pic", dto.getStore_cashier_desk_pic()); + data.put("settle_card_front_pic", dto.getSettle_card_front_pic()); + data.put("auth_enturst_pic", dto.getAuth_enturst_pic()); + + JSONObject body = new JSONObject(); + body.put("sys_id", huifu_id); + body.put("product_id", product_id); + body.put("sign", sign(data.toJSONString())); + body.put("data", data); + HttpRequest post = HttpUtil.createPost(url); + Map<String, String> headers = new HashMap<>(); + headers.put("Content-type","application/json; charset=utf-8"); + headers.put("Accept", "application/json"); + post.addHeaders(headers); + post.body(body.toJSONString()); + HttpResponse execute = post.execute(); + String result = execute.body(); + execute.close(); + if(!StringUtils.hasLength(result)){ + return R.fail("请求异常"); + } + JSONObject resultData = JSON.parseObject(result); + String resp_code = resultData.getString("resp_code"); + String resp_desc = resultData.getString("resp_desc"); + //处理成功 + String success1 = "00000000"; + //交易正在处理中 + String success2 = "00000100"; + //审核中 + String success3 = "90000000"; + MerchantBasicdataVo vo = new MerchantBasicdataVo(); + if(success1.equals(resp_code) || success2.equals(resp_code) || success3.equals(resp_code)){ + vo.setStatus(success1.equals(resp_code) ? 1 : success3.equals(resp_code) ? 2 : 3); + vo.setHuifu_id(resultData.getString("huifu_id")); + vo.setApply_no(resultData.getString("apply_no")); + vo.setToken_no(resultData.getString("token_no")); + return R.ok(vo); + } + return R.fail(resp_desc); + } + + + + /** + * 个人商户进件通知回调处理 + * @param jsonObject + * @return + */ + public static R<MerchantBasicdataVo> merchantBasicdataIndvNotify(JSONObject jsonObject){ + String ok = "200"; + String success = "10000"; + String resp_code = jsonObject.getString("resp_code"); + if(ok.equals(resp_code) || success.equals(resp_code)){ + JSONObject data = jsonObject.getJSONObject("data"); + String sub_resp_code = data.getString("sub_resp_code"); + String sub_resp_desc = data.getString("sub_resp_desc"); + //处理成功 + String success1 = "00000000"; + //交易正在处理中 + String success2 = "00000100"; + //审核中 + String success3 = "90000000"; + MerchantBasicdataVo vo = new MerchantBasicdataVo(); + if(success1.equals(sub_resp_code) || success2.equals(sub_resp_code) || success3.equals(sub_resp_code)){ + vo.setStatus(success1.equals(sub_resp_code) ? 1 : success3.equals(sub_resp_code) ? 2 : 3); + vo.setAudit_status(data.getString("audit_status")); + vo.setAudit_desc(data.getString("audit_desc")); + vo.setHuifu_id(data.getString("huifu_id")); + vo.setApply_no(data.getString("apply_no")); + vo.setToken_no(data.getString("token_no")); + return R.ok(vo); + } + return R.fail(sub_resp_desc); + } + return R.fail(jsonObject.getString("resp_desc")); + } + + + /** + * 上传文件 + * @param file_url 文件网络地址 + * @param req_seq_id 流水号 + * @param file_type 文件类型 + * @return + */ + public static R<String> supplementaryPicture(String file_url, String req_seq_id, String file_type){ + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); + String url = "https://api.huifu.com/v2/supplementary/picture"; + // 请求参数 + Map<String, Object> params = new HashMap<String, Object>(); + // 请求流水号,需保证当天商户下唯一,推荐采用日期时间+几位流水号的形式 + params.put("req_seq_id", req_seq_id); + params.put("req_date", sdf.format(new Date())); + params.put("file_type", file_type); + params.put("huifu_id", huifu_id); + String data = JSON.toJSONString(params); + File file = HttpUtil.downloadFileFromUrl(file_url, path); + + CloseableHttpClient httpclient1 = HttpClients.createDefault(); + CloseableHttpResponse response = null; + String result = null; + + try { + HttpPost httpPost = new HttpPost(url); + org.apache.http.entity.ContentType contentType = org.apache.http.entity.ContentType.create("text/plain", Charset.forName("UTF-8")); + MultipartEntityBuilder mEntityBuilder = MultipartEntityBuilder.create(); + mEntityBuilder.addTextBody("sys_id", huifu_id, contentType); + mEntityBuilder.addTextBody("product_id", product_id, contentType); + mEntityBuilder.addTextBody("data", data, contentType); + mEntityBuilder.addBinaryBody("file", file); + + httpPost.setEntity(mEntityBuilder.build()); + response = httpclient1.execute(httpPost); + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == org.apache.http.HttpStatus.SC_OK) { + HttpEntity resEntity = response.getEntity(); + result = EntityUtils.toString(resEntity); + System.out.print(result); + } + if(!StringUtils.hasLength(result)){ + return R.fail("请求异常"); + } + JSONObject results = JSON.parseObject(result); + JSONObject resultData = results.getJSONObject("data"); + String resp_code = resultData.getString("resp_code"); + String resp_desc = resultData.getString("resp_desc"); + //成功 + String success1 = "00000000"; + if(success1.equals(resp_code)){ + return R.ok(resultData.getString("file_id")); + } + return R.fail(resp_desc); + } catch (IOException e) { + e.printStackTrace(); + return R.fail("请求异常"); + } + } + + + /** + * 商户分账配置 + * @param req_seq_id 流水号 + * @param huifuId 商户号 + * @param apply_ratio 最大分账比例 + * @param async_return_url 回调通知 + * @return + */ + public static R<String> merchantSplitConfig(String req_seq_id, String huifuId, Double apply_ratio, String async_return_url){ + NumberFormat numberInstance = NumberFormat.getInstance(); + //最大两位小数 + numberInstance.setMaximumFractionDigits(2); + String url = "https://api.huifu.com/v2/merchant/split/config"; + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); + JSONObject data = new JSONObject(); + data.put("req_seq_id", req_seq_id); + data.put("req_date", sdf.format(new Date())); + data.put("huifu_id", huifuId); + data.put("rule_origin", "01"); + data.put("div_flag", "Y"); + data.put("apply_ratio", numberInstance.format(apply_ratio)); + data.put("start_type", "0"); + data.put("async_return_url", async_return_url); + data.put("scene", ""); + + JSONObject file_list = new JSONObject(); + file_list.put("file_id", ""); + data.put("file_list", file_list.toJSONString()); + + JSONObject split_ext_info = new JSONObject(); + split_ext_info.put("busi_instruction", ""); + split_ext_info.put("capital_instruction", ""); + split_ext_info.put("function_instruction", ""); + data.put("split_ext_info", split_ext_info.toJSONString()); + data.put("online_busi_type", ""); + + JSONObject body = new JSONObject(); + body.put("sys_id", huifu_id); + body.put("product_id", product_id); + body.put("sign", sign(data.toJSONString())); + body.put("data", data); + HttpRequest post = HttpUtil.createPost(url); + Map<String, String> headers = new HashMap<>(); + headers.put("Content-type","application/json; charset=utf-8"); + headers.put("Accept", "application/json"); + post.addHeaders(headers); + post.body(body.toJSONString()); + HttpResponse execute = post.execute(); + String result = execute.body(); + execute.close(); + if(!StringUtils.hasLength(result)){ + return R.fail("请求异常"); + } + JSONObject resultData = JSON.parseObject(result).getJSONObject("data"); + String resp_code = resultData.getString("resp_code"); + String resp_desc = resultData.getString("resp_desc"); + //处理成功 + String success1 = "00000000"; + //交易正在处理中 + String success2 = "00000100"; + //审核中 + String success3 = "90000000"; + if(success1.equals(resp_code) || success2.equals(resp_code) || success3.equals(resp_code)){ + return R.ok(resultData.getString("apply_no")); + } + return R.fail(resp_desc); + } + + + /** + * 商户分账配置回调处理 + * @param jsonObject + * @return + */ + public static R<MerchantBasicdataVo> merchantSplitConfigNotify(JSONObject jsonObject){ + String sub_resp_code = jsonObject.getString("sub_resp_code"); + String sub_resp_desc = jsonObject.getString("sub_resp_desc"); + //处理成功 + String success1 = "00000000"; + //交易正在处理中 + String success2 = "00000100"; + //审核中 + String success3 = "90000000"; + MerchantBasicdataVo vo = new MerchantBasicdataVo(); + if(success1.equals(sub_resp_code) || success2.equals(sub_resp_code) || success3.equals(sub_resp_code)){ + vo.setStatus(success1.equals(sub_resp_code) ? 1 : success3.equals(sub_resp_code) ? 2 : 3); + vo.setAudit_status(jsonObject.getString("audit_status")); + vo.setAudit_desc(jsonObject.getString("audit_desc")); + vo.setHuifu_id(jsonObject.getString("huifu_id")); + vo.setApply_no(jsonObject.getString("apply_no")); + return R.ok(vo); + } + return R.fail(sub_resp_desc); + } + +} -- Gitblit v1.7.1