diff --git a/ghy-admin/src/main/java/com/ghy/web/controller/customer/CustomerBankController.java b/ghy-admin/src/main/java/com/ghy/web/controller/customer/CustomerBankController.java new file mode 100644 index 00000000..16093567 --- /dev/null +++ b/ghy-admin/src/main/java/com/ghy/web/controller/customer/CustomerBankController.java @@ -0,0 +1,89 @@ +package com.ghy.web.controller.customer; + +import com.alibaba.fastjson.JSON; +import com.ghy.common.adapay.AdapayConfig; +import com.ghy.common.adapay.AdapayService; +import com.ghy.common.adapay.model.AdapayStatusEnum; +import com.ghy.common.adapay.model.Merchant; +import com.ghy.common.core.domain.AjaxResult; +import com.ghy.common.utils.AdapayUtils; +import com.ghy.customer.domain.CustomerBank; +import com.ghy.customer.request.BindBankCardRequest; +import com.ghy.customer.service.CustomerBankService; +import com.huifu.adapay.core.exception.BaseAdaPayException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import javax.annotation.Resource; +import java.util.Map; +import java.util.Set; + +/** + * @author HH 2022/5/20 + */ +@Slf4j +@Controller +@RequestMapping("customer/bankcard") +public class CustomerBankController { + + @Resource + private CustomerBankService customerBankService; + @Resource + private AdapayService adapayService; + + /** + * 个人账户绑定银行卡接口 + */ + @PostMapping("bind") + private AjaxResult bindBankCard(BindBankCardRequest request) throws BaseAdaPayException { + Set merchants = AdapayConfig.getMerchants(); + for (Merchant merchant : merchants) { + String memberId = AdapayUtils.getMemberId(request.getCustomerId(), merchant.getDeptId()); + // 先在Adapay创建实名用户 + Map result1 = adapayService.createMember(merchant.getDeptId(), memberId, request.getPhone(), + request.getName(), request.getCertId(), null, null, null, null); + if (!AdapayStatusEnum.succeeded.code.equals(result1.get("status"))) { + log.warn("实名认证失败[{}]", JSON.toJSONString(result1)); + return AjaxResult.error("个人信息不正确"); + } + + // 开始创建结算账户 + Map result2 = adapayService.createSettleAccount(merchant.getDeptId(), memberId, request.getCertId(), request.getName(), + "2", request.getCertId(), request.getPhone(), null, null, null); + if (!AdapayStatusEnum.succeeded.code.equals(result2.get("status"))) { + log.warn("创建结算账户失败[{}]", JSON.toJSONString(result1)); + return AjaxResult.error("个人信息与银行卡不匹配"); + } + + CustomerBank customerBank = new CustomerBank(); + customerBank.setCustomerId(request.getCustomerId()); + customerBank.setName(request.getName()); + customerBank.setCertId(request.getCertId()); + // TODO 入库 + } + return AjaxResult.success(); + } +} + + + + + + + + + + + + + + + + + + + + + diff --git a/ghy-admin/src/main/java/com/ghy/web/controller/pay/AlipayController.java b/ghy-admin/src/main/java/com/ghy/web/controller/pay/AlipayController.java index 2ddb76a7..9bb2fc25 100644 --- a/ghy-admin/src/main/java/com/ghy/web/controller/pay/AlipayController.java +++ b/ghy-admin/src/main/java/com/ghy/web/controller/pay/AlipayController.java @@ -45,7 +45,7 @@ public class AlipayController extends BaseController { // TODO 订单里需要补充支付金额、tittle、简要描述、分账信息、description PayParam payParam = new PayParam(orderMaster.getCode(), "orderMaster.get支付金额", "orderMaster.getTittle()", "orderMaster.get商品描述信息"); - map = adapayService.alipayQrPay(payParam, payCallback, null, null, null); + map = adapayService.alipayQrPay(orderMaster.getDeptId(), payParam, payCallback, null, null, null); } catch (BaseAdaPayException e) { logger.error("获取微信用户信息失败", e); return AjaxResult.error(); diff --git a/ghy-admin/src/main/java/com/ghy/web/controller/pay/WxPayController.java b/ghy-admin/src/main/java/com/ghy/web/controller/pay/WxPayController.java index e0bf4dde..153506bc 100644 --- a/ghy-admin/src/main/java/com/ghy/web/controller/pay/WxPayController.java +++ b/ghy-admin/src/main/java/com/ghy/web/controller/pay/WxPayController.java @@ -21,7 +21,6 @@ import org.springframework.web.bind.annotation.ResponseBody; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; -import java.io.IOException; import java.util.Map; @Controller @@ -65,7 +64,7 @@ public class WxPayController extends BaseController { // TODO 订单里需要补充支付金额、tittle、简要描述、分账信息、description PayParam payParam = new PayParam(orderMaster.getCode(), "0.01", "工圈子测试", "工圈子测试描述"); - map = adapayService.wxLitePay(payParam, payCallback, expend, null, null); + map = adapayService.wxLitePay(orderMaster.getDeptId(), payParam, payCallback, expend, null, null); } catch (BaseAdaPayException e) { logger.error("获取微信用户信息失败", e); return AjaxResult.error(); @@ -99,7 +98,7 @@ public class WxPayController extends BaseController { Map map; // TODO 订单里需要补充支付金额、tittle、简要描述、分账信息、description PayParam payParam = new PayParam("订单号", "支付金额", "商品标题", "商品描述信息"); - map = adapayService.wxPubPay(payParam, payCallback, expend, null, null); + map = adapayService.wxPubPay(123456789L, payParam, payCallback, expend, null, null); return AjaxResult.success(map); } catch (Exception e) { e.printStackTrace(); diff --git a/ghy-admin/src/main/java/com/ghy/web/core/AfterPay.java b/ghy-admin/src/main/java/com/ghy/web/core/AfterPay.java index 6d56e130..f58fdddf 100644 --- a/ghy-admin/src/main/java/com/ghy/web/core/AfterPay.java +++ b/ghy-admin/src/main/java/com/ghy/web/core/AfterPay.java @@ -25,6 +25,7 @@ public class AfterPay { return new PayCallback() { @Override public void onReply(PayReply reply) { + reply.getStatus(); // TODO 修改 OrderMaster 订单状态 // TODO 修改 OrderDetail 订单状态 // TODO 保存支付结果到MySQL diff --git a/ghy-admin/src/main/java/com/ghy/web/core/InitAdapay.java b/ghy-admin/src/main/java/com/ghy/web/core/InitAdapay.java new file mode 100644 index 00000000..78e9c6a9 --- /dev/null +++ b/ghy-admin/src/main/java/com/ghy/web/core/InitAdapay.java @@ -0,0 +1,48 @@ +package com.ghy.web.core; + +import com.ghy.common.adapay.AdapayConfig; +import com.ghy.common.adapay.AdapayProperties; +import com.ghy.common.adapay.model.Merchant; +import com.ghy.system.domain.SysDeptConfig; +import com.ghy.system.service.ISysDeptConfigService; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 初始化 Adapay + * + * @author HH 2022/5/21 + */ +@Component +public class InitAdapay { + + @Resource + AdapayProperties adapayProperties; + @Resource + ISysDeptConfigService deptConfigService; + + @PostConstruct + public void initAdapay() throws Exception { + + List deptConfigs = deptConfigService.selectAllMerchant(); + + Set merchants = deptConfigs.stream().map(x -> { + Merchant merchant = new Merchant(); + merchant.setDeptId(x.getDeptId()); + merchant.setAppId(x.getAdapayAppId()); + merchant.setApiKey(x.getAdapayApiKey()); + merchant.setMockApiKey(x.getAdapayMockApiKey()); + merchant.setRsaPrivateKey(x.getAdapayRsaPrivateKey()); + return merchant; + }).collect(Collectors.toSet()); + + AdapayConfig.setMerchants(merchants); + + AdapayConfig.initAdapay(adapayProperties); + } +} diff --git a/ghy-admin/src/main/resources/application.yaml b/ghy-admin/src/main/resources/application.yaml index 2c11721c..75d17b66 100644 --- a/ghy-admin/src/main/resources/application.yaml +++ b/ghy-admin/src/main/resources/application.yaml @@ -158,11 +158,7 @@ qiniu: adapay: debug: true prod-mode: false - appId: 'app_01af32e7-6173-414f-88e5-79cae64d5e24' notifyUrl: 'http://www.opsoul.com/adapay/callback' - apiKey: 'api_live_93a2fb4f-a74a-416f-967d-68557bcde43f' - mockApiKey: 'api_test_88bf2958-583d-41cd-a987-01fe767ff056' - rsaPrivateKey: 'MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMTk5xzz3KXA+waawr1DbU/SaSXZN8tY/22qHON7AAsp+yJ1/JCt+sD6/mpIUGittBS4n69t/9R5+2uHtD5/tVH7hYpJpVixLQw8/lonLPbeLFLgTMBHKJwSytBZXC2delnewHO/Zg6WlTWkdQB7gr73m7/wu2Ss+FDwGq6Q8sXdAgMBAAECgYEAtl8LTr72DjWsjdaFMEcnFftf12XWjyx1Ev+xWGcSiESvT6EXem8bxun1Az7N89eI6HSFvDlX8Fe4MEZ3BjjGGXSEXh3BYg5jI9YY/x4NdPvCxxVf9gGmBo2uBjkPoqYE8IGfpxnF+C4CBEyI5FPjhQRYB7aPKL7hImoCFkaFG4UCQQDojfLHd2ON2sgZdeAML9jxNzf1CBsGVfDrI7GOj+enWG+lWjG5tav/essfDOlZ6rZslyquLQZAGQqZz06cVCMzAkEA2L6WuCiEop9gsnGk03UcO8u54jFC68+2IA4bJeqicFmqMgs73PzsnNZ7t31q51iGsKxvCm3hziTGHGGH9TZyrwJATda1XG5ptCF2uI7r3yhkxNhmsm10HjrF2O6pj747G5hORlpaKn7Ugz7mng4ETURyqwYuEv6fCPVYxwLMnSbMYQJBAMggMkoYH1+IiWA6TlZw64DKuvd/RKs3PpKac7auzw2tvNg4Ry3k2xR1dgYWZ3703miCzoRysOwGSGYsJ7ziaUECQQC1RqZ0UBtEbKSr9fRYxo/lg28ioTlxoMjSL2OumEyJk+2t0S3gPQq5Wz+ylcwnK3Md0kGTyYs5kkTWPyPm9V1F' jim: appKey: '' diff --git a/ghy-common/src/main/java/com/ghy/common/adapay/AdapayAutoConfiguration.java b/ghy-common/src/main/java/com/ghy/common/adapay/AdapayAutoConfiguration.java deleted file mode 100644 index 1266f0e6..00000000 --- a/ghy-common/src/main/java/com/ghy/common/adapay/AdapayAutoConfiguration.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.ghy.common.adapay; - -import com.huifu.adapay.Adapay; -import com.huifu.adapay.model.MerConfig; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.util.Assert; - -/** - * Adapay 自动装载 - * - * @author HH 2022/3/25 - */ -@Configuration -@EnableConfigurationProperties(AdapayProperties.class) -public class AdapayAutoConfiguration { - - @Bean - public AdapayService adapayService(AdapayProperties adapayProperties) throws Exception { - - Assert.hasText(adapayProperties.getAppId(), "Adapay.appId is blank!"); - Assert.hasText(adapayProperties.getApiKey(), "Adapay.apiKey is blank!"); - Assert.hasText(adapayProperties.getMockApiKey(), "Adapay.mockApiKey is blank!"); - Assert.hasText(adapayProperties.getRsaPrivateKey(), "Adapay.rsaPrivateKey is blank!"); - - MerConfig merConfig = new MerConfig(); - merConfig.setApiKey(adapayProperties.getApiKey()); - merConfig.setApiMockKey(adapayProperties.getMockApiKey()); - merConfig.setRSAPrivateKey(adapayProperties.getRsaPrivateKey()); - Adapay.initWithMerConfig(merConfig); - - AdapayService adapayService = new AdapayService(); - adapayService.setAdapayProperties(adapayProperties); - return adapayService; - } -} \ No newline at end of file diff --git a/ghy-common/src/main/java/com/ghy/common/adapay/AdapayConfig.java b/ghy-common/src/main/java/com/ghy/common/adapay/AdapayConfig.java new file mode 100644 index 00000000..03027edc --- /dev/null +++ b/ghy-common/src/main/java/com/ghy/common/adapay/AdapayConfig.java @@ -0,0 +1,73 @@ +package com.ghy.common.adapay; + +import com.ghy.common.adapay.model.Merchant; +import com.huifu.adapay.Adapay; +import com.huifu.adapay.model.MerConfig; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.Assert; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Adapay 配置 + * + * @author HH 2022/3/25 + */ +@Configuration +@EnableConfigurationProperties(AdapayProperties.class) +public class AdapayConfig { + + /** + * 商户配置 + */ + private static Set merchants; + + /** + * deptId -> Merchant + */ + private static Map merchantMap; + + public static void initAdapay(AdapayProperties properties) throws Exception { + Adapay.debug = properties.isDebug(); + Adapay.prodMode = properties.isProdMode(); + Assert.hasText(properties.getNotifyUrl(), "NotifyUrl is blank!"); + Assert.notEmpty(merchants, "Merchants is empty!"); + Map configPathMap = new HashMap<>(merchants.size()); + for (Merchant merchant : merchants) { + check(merchant); + MerConfig merConfig = new MerConfig(); + merConfig.setApiKey(merchant.getApiKey()); + merConfig.setApiMockKey(merchant.getMockApiKey()); + merConfig.setRSAPrivateKey(merchant.getRsaPrivateKey()); + configPathMap.put(merchant.getDeptId().toString(), merConfig); + } + Adapay.initWithMerConfigs(configPathMap); + } + + private static void check(Merchant merchant) { + Assert.notNull(merchant.getDeptId(), "DeptId is null!"); + Assert.hasText(merchant.getAppId(), "AppId is blank!"); + Assert.hasText(merchant.getApiKey(), "ApiKey is blank!"); + Assert.hasText(merchant.getMockApiKey(), "MockApiKey is blank!"); + Assert.hasText(merchant.getRsaPrivateKey(), "RsaPrivateKey is blank!"); + } + + public static void setMerchants(Set merchants) { + AdapayConfig.merchants = merchants; + AdapayConfig.merchantMap = merchants.stream().collect(Collectors.toMap(Merchant::getDeptId, x -> x)); + } + + public static String getAppId(Long deptId) { + Merchant merchant = merchantMap.get(deptId); + Assert.notNull(merchant, String.format("Merchant[%s] notfound!", deptId)); + return merchant.getAppId(); + } + + public static Set getMerchants() { + return merchants; + } +} \ No newline at end of file diff --git a/ghy-common/src/main/java/com/ghy/common/adapay/AdapayProperties.java b/ghy-common/src/main/java/com/ghy/common/adapay/AdapayProperties.java index 96b4b25e..676e8799 100644 --- a/ghy-common/src/main/java/com/ghy/common/adapay/AdapayProperties.java +++ b/ghy-common/src/main/java/com/ghy/common/adapay/AdapayProperties.java @@ -10,7 +10,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * @author HH 2022/3/25 */ @Data -@Slf4j @ConfigurationProperties("adapay") public class AdapayProperties { @@ -24,17 +23,6 @@ public class AdapayProperties { */ private boolean prodMode = true; - private String appId; private String notifyUrl; - /** - * 初始化商户配置,服务器启动前,必须通过该方式初始化商户配置完成 - * apiKey为prod模式的API KEY - * mockApiKey为mock模式的API KEY - * rsaPrivateKey为商户发起请求时,用于请求参数加签所需要的RSA私钥 - */ - private String apiKey; - private String mockApiKey; - private String rsaPrivateKey; - } \ No newline at end of file diff --git a/ghy-common/src/main/java/com/ghy/common/adapay/AdapayService.java b/ghy-common/src/main/java/com/ghy/common/adapay/AdapayService.java index 9cbdbecc..4f51bdcc 100644 --- a/ghy-common/src/main/java/com/ghy/common/adapay/AdapayService.java +++ b/ghy-common/src/main/java/com/ghy/common/adapay/AdapayService.java @@ -10,10 +10,10 @@ import com.ghy.common.adapay.callback.mapping.RefundReplyMapping; import com.ghy.common.adapay.model.*; import com.huifu.adapay.core.exception.BaseAdaPayException; import com.huifu.adapay.model.*; -import lombok.Setter; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -27,7 +27,7 @@ import java.util.Map; /** * @author HH 2022/3/25 */ -@Setter +@Service public class AdapayService { protected final Logger logger = LoggerFactory.getLogger(this.getClass()); @@ -78,6 +78,7 @@ public class AdapayService { * 创建余额支付请求 * 商户利用该接口进行余额支付,支持同一商户下的商户-用户,用户-商户,用户-用户间的账户余额支付 * + * @param deptId [必填]商户ID * @param orderNo [必填]请求订单号,只能为英文、数字或者下划线的一种或多种组合,保证在app_id下唯一 * @param outMemberId [必填]出账用户的member_id, 若为商户本身时,请传入0 * @param inMemberId [必填]入账用户的member_id, 若为商户本身时,请传入0 @@ -90,11 +91,13 @@ public class AdapayService { * @param divMembers 分账对象信息列表,最多仅支持7个分账方 * @return 成功时同步返回交易结果的 JSON */ - public Map balancePay(@NotNull String orderNo, @NotNull String outMemberId, @NotNull String inMemberId, - @NotNull String transAmt, @NotNull String goodsTitle, @NotNull String goodsDesc, - String payMode, Collection divMembers) throws BaseAdaPayException { + public Map balancePay(@NotNull Long deptId, @NotNull String orderNo, @NotNull String outMemberId, + @NotNull String inMemberId, @NotNull String transAmt, @NotNull String goodsTitle, + @NotNull String goodsDesc, String payMode, Collection divMembers) throws BaseAdaPayException { + // 获取商户的appId + String appId = AdapayConfig.getAppId(deptId); Map balanceParam = new HashMap<>(10); - balanceParam.put("app_id", adapayProperties.getAppId()); + balanceParam.put("app_id", appId); balanceParam.put("adapay_func_code", "settle_accounts.balancePay"); balanceParam.put("order_no", orderNo); balanceParam.put("out_member_id", outMemberId); @@ -115,6 +118,7 @@ public class AdapayService { * 用户创建对私结算账户时,会对银行卡号、银行卡开户姓名、身份证号三要素认证,若认证失败,则创建结算账户失败。 * 每个结算账户对象 Adapay 系统会生成一个唯一的 id,可用于查询结算账户对象,或者删除结算账户对象。 * + * @param deptId [必填]商户ID * @param memberId [必填]商户下的用户id,只能为英文、数字或者下划线的一种或多种组合,保证在app_id下唯一 * @param cardId [必填]银行卡号 * @param cardName [必填]银行卡对应的户名 @@ -126,10 +130,12 @@ public class AdapayService { * @param areaCode 银行账户开户银行所在地区编码(省市编码),银行账户类型为对公时必填,省市编码详见area.json * @return 成功时同步返回一个包含 SettleAccount对象 的 JSON。 */ - public Map createSettleAccount(@NotNull String memberId, @NotNull String cardId, @NotNull String cardName, - @NotNull String bankAcctType, String certId, String telNo, + public Map createSettleAccount(@NotNull Long deptId, @NotNull String memberId, @NotNull String cardId, + @NotNull String cardName, @NotNull String bankAcctType, String certId, String telNo, String bankCode, String provCode, String areaCode) throws BaseAdaPayException { + // 获取商户的appId + String appId = AdapayConfig.getAppId(deptId); // 结算账户信息 参见结算账户信息(AccountInfo)对象 https://docs.adapay.tech/api/appendix.html#accountinfo Map accountInfo = new HashMap<>(9); @@ -157,7 +163,7 @@ public class AdapayService { Map settleCountParams = new HashMap<>(4); settleCountParams.put("member_id", memberId); - settleCountParams.put("app_id", adapayProperties.getAppId()); + settleCountParams.put("app_id", appId); // 目前仅支持:bank_account(银行卡) settleCountParams.put("channel", "bank_account"); settleCountParams.put("account_info", accountInfo); @@ -167,6 +173,7 @@ public class AdapayService { /** * 创建实名用户 https://docs.adapay.tech/api/trade.html#member-realname * + * @param deptId [必填]商户ID * @param memberId [必填]商户下的用户id,只能为英文、数字或者下划线的一种或多种组合,保证在app_id下唯一 * @param telNo [必填]用户手机号 * @param username [必填]用户姓名 @@ -177,11 +184,14 @@ public class AdapayService { * @param nickname 用户昵称 * @return 成功时同步返回一个包含Member对象的JSON */ - public Map createMember(String memberId, String telNo, String username, String certId, - String location, String email, String gender, String nickname) throws BaseAdaPayException { + public Map createMember(@NotNull Long deptId, @NotNull String memberId, @NotNull String telNo, + @NotNull String username, @NotNull String certId, String location, + String email, String gender, String nickname) throws BaseAdaPayException { + // 获取商户的appId + String appId = AdapayConfig.getAppId(deptId); Map memberParams = new HashMap<>(7); memberParams.put("member_id", memberId); - memberParams.put("app_id", adapayProperties.getAppId()); + memberParams.put("app_id", appId); memberParams.put("location", location); memberParams.put("email", email); memberParams.put("gender", gender); @@ -199,6 +209,7 @@ public class AdapayService { /** * 对指定商户或者商户下用户的结算账户可用余额发起主动取现操作,金额从账户中提到绑定的结算银行卡中 * + * @param deptId [必填]商户ID * @param callback [必填项]处理提现结果的接口 * @param orderNo [必填项]请求订单号,只能为英文、数字或者下划线的一种或多种组合,保证在app_id下唯一 * @param cashType [必填项]取现类型:T1-T+1取现;D1-D+1取现;D0-即时取现。 @@ -208,8 +219,10 @@ public class AdapayService { * @param feeMode 手续费收取模式:O-商户手续费账户扣取手续费,I-交易金额中扣取手续费;值为空时,默认值为I; * @return https://docs.adapay.tech/api/wallet.html#cash-response */ - public Map drawCash(DrawCashCallback callback, String orderNo, String cashType, String cashAmt, + public Map drawCash(@NotNull Long deptId, DrawCashCallback callback, String orderNo, String cashType, String cashAmt, String memberId, String remark, String feeMode) throws BaseAdaPayException { + // 获取商户的appId + String appId = AdapayConfig.getAppId(deptId); Assert.notNull(callback, "callback is null!"); Assert.hasText(orderNo, "orderNo is blank!"); Assert.hasText(cashType, "cashType is blank!"); @@ -217,7 +230,7 @@ public class AdapayService { Assert.hasText(memberId, "memberId is blank!"); Map cashParam = new HashMap<>(5); cashParam.put("order_no", orderNo); - cashParam.put("app_id", adapayProperties.getAppId()); + cashParam.put("app_id", appId); cashParam.put("cash_type", cashType); cashParam.put("cash_amt", cashAmt); cashParam.put("member_id", memberId); @@ -231,28 +244,34 @@ public class AdapayService { /** * 支付宝正扫支付 */ - public Map alipayQrPay(PayParam payParam, PayCallback callback, AlipayExpend expend, DeviceInfo deviceInfo, + public Map alipayQrPay(@NotNull Long deptId, PayParam payParam, PayCallback callback, + AlipayExpend expend, DeviceInfo deviceInfo, Collection divMembers) throws BaseAdaPayException { - return pay("alipay_qr", payParam, callback, expend, deviceInfo, divMembers); + return pay(deptId, "alipay_qr", payParam, callback, expend, deviceInfo, divMembers); } /** * 微信公众号支付 */ - public Map wxPubPay(PayParam payParam, PayCallback callback, WxpayExpend expend, DeviceInfo deviceInfo, + public Map wxPubPay(@NotNull Long deptId, PayParam payParam, PayCallback callback, + WxpayExpend expend, DeviceInfo deviceInfo, Collection divMembers) throws BaseAdaPayException { - return pay("wx_pub", payParam, callback, expend, deviceInfo, divMembers); + return pay(deptId, "wx_pub", payParam, callback, expend, deviceInfo, divMembers); } /** * 微信小程序支付 */ - public Map wxLitePay(PayParam payParam, PayCallback callback, WxpayExpend expend, DeviceInfo deviceInfo, + public Map wxLitePay(@NotNull Long deptId, PayParam payParam, PayCallback callback, + WxpayExpend expend, DeviceInfo deviceInfo, Collection divMembers) throws BaseAdaPayException { - return pay("wx_lite", payParam, callback, expend, deviceInfo, divMembers); + return pay(deptId, "wx_lite", payParam, callback, expend, deviceInfo, divMembers); } /** + * 聚合支付 + * + * @param deptId [必填]商户ID * @param payChannel [必填项]支付渠道,详见 https://docs.adapay.tech/api/appendix.html#id2 * @param payParam [必填项]支付参数 * @param callback [必填项]处理支付结果的回调接口 @@ -261,10 +280,13 @@ public class AdapayService { * @param divMembers 分账对象信息列表 https://docs.adapay.tech/api/appendix.html#divmembers * @return 同步返回一个 支付对象,详见 https://docs.adapay.tech/api/trade.html#id2 */ - public Map pay(String payChannel, PayParam payParam, PayCallback callback, Expend expend, - DeviceInfo deviceInfo, Collection divMembers) throws BaseAdaPayException { + public Map pay(@NotNull Long deptId, @NotNull String payChannel, @NotNull PayParam payParam, + @NotNull PayCallback callback, Expend expend, DeviceInfo deviceInfo, + Collection divMembers) throws BaseAdaPayException { + // 获取商户的appId + String appId = AdapayConfig.getAppId(deptId); JSONObject paymentParams = payParam.toJSONObject(); - paymentParams.put("app_id", adapayProperties.getAppId()); + paymentParams.put("app_id", appId); paymentParams.put("notify_url", adapayProperties.getNotifyUrl()); paymentParams.put("pay_channel", payChannel); paymentParams.put("div_members", divMembers); @@ -289,7 +311,8 @@ public class AdapayService { * @param refundAmt [必填项]退款金额,若退款金额小于原交易金额,则认为是部分退款,必须大于0,保留两位小数点,如0.10、100.05等 * @return 同步返回一个 退款对象 https://docs.adapay.tech/api/trade.html#create-refund-params */ - public Map refund(RefundCallback callback, String paymentId, String refundOrderNo, String refundAmt) throws BaseAdaPayException { + public Map refund(@NotNull RefundCallback callback, @NotNull String paymentId, + @NotNull String refundOrderNo, @NotNull String refundAmt) throws BaseAdaPayException { Assert.notNull(callback, "callback is null!"); Assert.hasText(paymentId, "paymentId is blank!"); Assert.hasText(refundOrderNo, "refundOrderNo is blank!"); diff --git a/ghy-common/src/main/java/com/ghy/common/adapay/model/AdapayStatusEnum.java b/ghy-common/src/main/java/com/ghy/common/adapay/model/AdapayStatusEnum.java new file mode 100644 index 00000000..7c13df32 --- /dev/null +++ b/ghy-common/src/main/java/com/ghy/common/adapay/model/AdapayStatusEnum.java @@ -0,0 +1,21 @@ +package com.ghy.common.adapay.model; + +/** + * Adapay 的接口中常用到 status 参数 + * + * @author HH 2022/5/21 + */ +public enum AdapayStatusEnum { + + pending("pending", "处理中"), + succeeded("succeeded", "成功"), + failed("failed", "失败"); + + public String code; + public String description; + + AdapayStatusEnum(String code, String description) { + this.code = code; + this.description = description; + } +} diff --git a/ghy-common/src/main/java/com/ghy/common/adapay/model/Merchant.java b/ghy-common/src/main/java/com/ghy/common/adapay/model/Merchant.java new file mode 100644 index 00000000..b5c44244 --- /dev/null +++ b/ghy-common/src/main/java/com/ghy/common/adapay/model/Merchant.java @@ -0,0 +1,52 @@ +package com.ghy.common.adapay.model; + +import lombok.Getter; +import lombok.Setter; + +import java.util.Objects; + +/** + * Adapay商户配置 + * + * @author HH 2022/5/20 + */ +@Getter +@Setter +public class Merchant { + + /** + * 商户唯一标识 + */ + private Long deptId; + + private String appId; + + /** + * prod模式的 api_key + */ + private String apiKey; + + /** + * mock模式的 api_key + */ + private String mockApiKey; + + /** + * 商户发起请求时用于请求参数加签所需要的RSA私钥 + */ + private String rsaPrivateKey; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Merchant merchant = (Merchant) o; + // deptId 和 appId 任何一个相同都视为同一个商户 + return deptId.equals(merchant.deptId) || appId.equals(merchant.appId); + } + + @Override + public int hashCode() { + return Objects.hash(deptId, appId); + } +} diff --git a/ghy-common/src/main/java/com/ghy/common/utils/AdapayUtils.java b/ghy-common/src/main/java/com/ghy/common/utils/AdapayUtils.java new file mode 100644 index 00000000..374534c9 --- /dev/null +++ b/ghy-common/src/main/java/com/ghy/common/utils/AdapayUtils.java @@ -0,0 +1,16 @@ +package com.ghy.common.utils; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.Assert; + +/** + * @author HH 2022/5/21 + */ +@Slf4j +public class AdapayUtils { + + public static String getMemberId(Long memberId, Long deptId) { + Assert.isTrue(Math.min(memberId, deptId) > 0, "Invalid [memberId] or [deptId]"); + return String.format("C%dD%d", memberId, deptId); + } +} diff --git a/ghy-common/src/main/resources/META-INF/spring.factories b/ghy-common/src/main/resources/META-INF/spring.factories index d4d5a584..438291b5 100644 --- a/ghy-common/src/main/resources/META-INF/spring.factories +++ b/ghy-common/src/main/resources/META-INF/spring.factories @@ -1,3 +1,3 @@ # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -com.ghy.common.adapay.AdapayAutoConfiguration +com.ghy.common.adapay.AdapayConfig diff --git a/ghy-custom/src/main/java/com/ghy/customer/domain/CustomerBank.java b/ghy-custom/src/main/java/com/ghy/customer/domain/CustomerBank.java index 8a08e1f4..3e1f5c44 100644 --- a/ghy-custom/src/main/java/com/ghy/customer/domain/CustomerBank.java +++ b/ghy-custom/src/main/java/com/ghy/customer/domain/CustomerBank.java @@ -6,6 +6,7 @@ import lombok.Data; /** * 消费者银行卡 + * * @author clunt */ @Data @@ -20,6 +21,9 @@ public class CustomerBank extends BaseEntity { @Excel(name = "用户真实姓名", cellType = Excel.ColumnType.STRING) private String name; + @Excel(name = "身份证号", cellType = Excel.ColumnType.STRING) + private String certId; + @Excel(name = "银行名称", cellType = Excel.ColumnType.STRING) private String bankName; @@ -29,4 +33,12 @@ public class CustomerBank extends BaseEntity { @Excel(name = "银行卡绑定手机号", cellType = Excel.ColumnType.STRING) private String phone; + @Excel(name = "分公司id", cellType = Excel.ColumnType.NUMERIC) + private Long deptId; + + @Excel(name = "adapay会员账号", cellType = Excel.ColumnType.STRING) + private String adapayMemberId; + + @Excel(name = "是否为结算账户", cellType = Excel.ColumnType.STRING) + private Integer settleAccount; } diff --git a/ghy-custom/src/main/java/com/ghy/customer/mapper/CustomerBankMapper.java b/ghy-custom/src/main/java/com/ghy/customer/mapper/CustomerBankMapper.java new file mode 100644 index 00000000..455a4d28 --- /dev/null +++ b/ghy-custom/src/main/java/com/ghy/customer/mapper/CustomerBankMapper.java @@ -0,0 +1,66 @@ +package com.ghy.customer.mapper; + +import com.ghy.customer.domain.CustomerBank; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 消费者银行卡 + * + * @author HH 2022/5/20 + */ +@Mapper +public interface CustomerBankMapper { + + /** + * @param customerBank 消费者银行卡筛选条件 + * @return 符合结果消费者银行卡 + */ + List getCustomerBankList(CustomerBank customerBank); + + /** + * 用消费者ID查银行卡信息 + * + * @param customerId 消费者ID + */ + List selectByCustomerId(Long customerId); + + /** + * 用消费者ID和部门ID查银行卡信息 + * + * @param customerId 消费者ID + * @param deptId 部门ID + */ + List selectByCustomerIdAndDeptId(Long customerId, Long deptId); + + /** + * @param customerBankId 消费者银行卡id + * @return 消费者银行卡信息 + */ + CustomerBank selectByCustomerBankId(Long customerBankId); + + /** + * @param customerBankIds 消费者银行卡ids + * @return 删除成功条数 + */ + int deleteByIds(Long[] customerBankIds); + + /** + * @param customerBankId 消费者银行卡id + * @return 删除成功条数 + */ + int deleteByCustomerBankId(Long customerBankId); + + /** + * @param customerBank 消费者银行卡对象 + * @return 入库成功条数 + */ + int insertCustomerBank(CustomerBank customerBank); + + /** + * @param customerBank 消费者银行卡对象 + * @return 更新成功条数 + */ + int updateCustomerBank(CustomerBank customerBank); +} diff --git a/ghy-custom/src/main/java/com/ghy/customer/request/BindBankCardRequest.java b/ghy-custom/src/main/java/com/ghy/customer/request/BindBankCardRequest.java new file mode 100644 index 00000000..7cb0c1ae --- /dev/null +++ b/ghy-custom/src/main/java/com/ghy/customer/request/BindBankCardRequest.java @@ -0,0 +1,37 @@ +package com.ghy.customer.request; + +import lombok.Data; + +/** + * 个人账户绑定银行卡接口参数 + * + * @author HH 2022/5/20 + */ +@Data +public class BindBankCardRequest { + + /** + * 消费者ID + */ + private Long customerId; + + /** + * 用户真实姓名 + */ + private String name; + + /** + * 身份证号 + */ + private String certId; + + /** + * 银行卡号 + */ + private String bankNum; + + /** + * 银行卡绑定手机号 + */ + private String phone; +} diff --git a/ghy-custom/src/main/java/com/ghy/customer/service/CustomerBankService.java b/ghy-custom/src/main/java/com/ghy/customer/service/CustomerBankService.java new file mode 100644 index 00000000..ec6dcf06 --- /dev/null +++ b/ghy-custom/src/main/java/com/ghy/customer/service/CustomerBankService.java @@ -0,0 +1,58 @@ +package com.ghy.customer.service; + +import com.ghy.customer.domain.CustomerBank; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 消费者银行卡 + * + * @author HH 2022/5/20 + */ +public interface CustomerBankService { + + /** + * @param customerBank 消费者银行卡筛选条件 + * @return 符合结果消费者银行卡 + */ + List getCustomerBankList(CustomerBank customerBank); + + /** + * 用消费者ID和部门ID查银行卡信息 + * + * @param customerId 消费者ID + * @param deptId 部门ID + */ + List selectByCustomerIdAndDeptId(Long customerId, Long deptId); + + /** + * @param customerBankId 消费者银行卡id + * @return 消费者银行卡信息 + */ + CustomerBank selectByCustomerBankId(Long customerBankId); + + /** + * @param customerBankIds 消费者银行卡ids + * @return 删除成功条数 + */ + int deleteByIds(Long[] customerBankIds); + + /** + * @param customerBankId 消费者银行卡id + * @return 删除成功条数 + */ + int deleteByCustomerBankId(Long customerBankId); + + /** + * @param customerBank 消费者银行卡对象 + * @return 入库成功条数 + */ + int insertCustomerBank(CustomerBank customerBank); + + /** + * @param customerBank 消费者银行卡对象 + * @return 更新成功条数 + */ + int updateCustomerBank(CustomerBank customerBank); +} diff --git a/ghy-custom/src/main/java/com/ghy/customer/service/impl/CustomerBankServiceImpl.java b/ghy-custom/src/main/java/com/ghy/customer/service/impl/CustomerBankServiceImpl.java new file mode 100644 index 00000000..ea4f5203 --- /dev/null +++ b/ghy-custom/src/main/java/com/ghy/customer/service/impl/CustomerBankServiceImpl.java @@ -0,0 +1,57 @@ +package com.ghy.customer.service.impl; + +import com.ghy.common.utils.AdapayUtils; +import com.ghy.customer.domain.CustomerBank; +import com.ghy.customer.mapper.CustomerBankMapper; +import com.ghy.customer.service.CustomerBankService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; + +/** + * @author HH 2022/5/20 + */ +@Service +public class CustomerBankServiceImpl implements CustomerBankService { + + @Resource + CustomerBankMapper customerBankMapper; + + @Override + public List getCustomerBankList(CustomerBank customerBank) { + return customerBankMapper.getCustomerBankList(customerBank); + } + + @Override + public List selectByCustomerIdAndDeptId(Long customerId, Long deptId) { + return customerBankMapper.selectByCustomerIdAndDeptId(customerId, deptId); + } + + @Override + public CustomerBank selectByCustomerBankId(Long customerBankId) { + return customerBankMapper.selectByCustomerBankId(customerBankId); + } + + @Override + public int deleteByIds(Long[] customerBankIds) { + return customerBankMapper.deleteByIds(customerBankIds); + } + + @Override + public int deleteByCustomerBankId(Long customerBankId) { + return customerBankMapper.deleteByCustomerBankId(customerBankId); + } + + @Override + public int insertCustomerBank(CustomerBank customerBank) { + customerBank.setAdapayMemberId(AdapayUtils.getMemberId(customerBank.getCustomerId(), customerBank.getDeptId())); + return customerBankMapper.insertCustomerBank(customerBank); + } + + @Override + public int updateCustomerBank(CustomerBank customerBank) { + return customerBankMapper.updateCustomerBank(customerBank); + } + +} \ No newline at end of file diff --git a/ghy-custom/src/main/resources/mapper/customer/CustomerBankMapper.xml b/ghy-custom/src/main/resources/mapper/customer/CustomerBankMapper.xml new file mode 100644 index 00000000..ff3dc8ef --- /dev/null +++ b/ghy-custom/src/main/resources/mapper/customer/CustomerBankMapper.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + SELECT customer_bank_id, + customer_id, + name, + cert_id, + bank_name, + bank_num, + phone, + dept_id, + adapay_member_id, + settle_account, + create_by, + create_time, + remark + FROM customer_bank + + + + UPDATE customer_bank + + customer_id = #{customerId}, + `name` = #{name}, + bank_name = #{bankName}, + bank_num = #{bankNum}, + phone = #{phone}, + dept_id = #{deptId}, + settle_account = #{settleAccount}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + WHERE customer_bank_id = #{customerBankId} + + + + INSERT INTO customer_bank( + customer_id, + name, + cert_id, + bank_name, + bank_num, + phone, + dept_id, + adapay_member_id, + settle_account, + create_by, + remark, + create_time + )VALUES( + #{customerId}, + #{name}, + #{certId}, + #{bankName}, + #{bankNum}, + #{phone}, + #{deptId}, + #{adapayMemberId}, + #{settleAccount}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + + + DELETE FROM customer_bank WHERE customer_bank_id IN + + #{customerBankId} + + + + + DELETE + FROM customer_bank + WHERE customer_bank_id = #{customerBankId} + + + + + + + + + diff --git a/ghy-order/src/main/java/com/ghy/order/domain/OrderMaster.java b/ghy-order/src/main/java/com/ghy/order/domain/OrderMaster.java index ec42076e..24b751db 100644 --- a/ghy-order/src/main/java/com/ghy/order/domain/OrderMaster.java +++ b/ghy-order/src/main/java/com/ghy/order/domain/OrderMaster.java @@ -16,6 +16,9 @@ public class OrderMaster extends BaseEntity { @Excel(name = "序号", cellType = Excel.ColumnType.NUMERIC) private Long id; + @Excel(name = "商户ID", cellType = Excel.ColumnType.NUMERIC) + private Long deptId; + @Excel(name = "订单编码", cellType = Excel.ColumnType.STRING) private String code; @@ -28,7 +31,7 @@ public class OrderMaster extends BaseEntity { @Excel(name = "订单状态", cellType = Excel.ColumnType.NUMERIC) private Integer orderStatus; - @Excel(name = "付款类型",cellType = Excel.ColumnType.NUMERIC) + @Excel(name = "付款类型", cellType = Excel.ColumnType.NUMERIC) private Integer payType; @Excel(name = "付款状态", cellType = Excel.ColumnType.NUMERIC) diff --git a/ghy-system/src/main/java/com/ghy/system/domain/SysDeptConfig.java b/ghy-system/src/main/java/com/ghy/system/domain/SysDeptConfig.java index 837da6ea..279dfc4a 100644 --- a/ghy-system/src/main/java/com/ghy/system/domain/SysDeptConfig.java +++ b/ghy-system/src/main/java/com/ghy/system/domain/SysDeptConfig.java @@ -20,10 +20,17 @@ public class SysDeptConfig extends BaseEntity { @Excel(name = "首页图片", cellType = Excel.ColumnType.STRING) private String bannerUrl; - //通用支付/小程序配置 - private String adapayAppKey; - private String adapayMasterSecret; + // Adapay支付 + // apiKey为prod模式的API KEY + // mockApiKey为mock模式的API KEY + // rsaPrivateKey为商户发起请求时,用于请求参数加签所需要的RSA私钥 + private String adapayAppId; + private String adapayApiKey; + private String adapayMockApiKey; + private String adapayRsaPrivateKey; private String adapayMaxRetryTimes; + + //微信配置 private String wxAppId; private String wxSecret; diff --git a/ghy-system/src/main/java/com/ghy/system/mapper/SysDeptConfigMapper.java b/ghy-system/src/main/java/com/ghy/system/mapper/SysDeptConfigMapper.java index 5bfba886..f3f4fc14 100644 --- a/ghy-system/src/main/java/com/ghy/system/mapper/SysDeptConfigMapper.java +++ b/ghy-system/src/main/java/com/ghy/system/mapper/SysDeptConfigMapper.java @@ -2,6 +2,8 @@ package com.ghy.system.mapper; import com.ghy.system.domain.SysDeptConfig; +import java.util.List; + /** * @author clunt * 部门配置Mapper层 @@ -15,4 +17,9 @@ public interface SysDeptConfigMapper { */ public SysDeptConfig selectByDeptId(Long deptId); + /** + * 获取所有商户的配置 + */ + List selectAllMerchant(); + } diff --git a/ghy-system/src/main/java/com/ghy/system/service/ISysDeptConfigService.java b/ghy-system/src/main/java/com/ghy/system/service/ISysDeptConfigService.java index 792c18ae..9b66a82e 100644 --- a/ghy-system/src/main/java/com/ghy/system/service/ISysDeptConfigService.java +++ b/ghy-system/src/main/java/com/ghy/system/service/ISysDeptConfigService.java @@ -2,6 +2,8 @@ package com.ghy.system.service; import com.ghy.system.domain.SysDeptConfig; +import java.util.List; + /** * @author clunt * 部门配置Service层 @@ -14,4 +16,9 @@ public interface ISysDeptConfigService { */ public SysDeptConfig selectByDeptId(Long deptId); + /** + * 获取所有商户的配置 + */ + List selectAllMerchant(); + } diff --git a/ghy-system/src/main/java/com/ghy/system/service/impl/SysDeptConfigServiceImpl.java b/ghy-system/src/main/java/com/ghy/system/service/impl/SysDeptConfigServiceImpl.java index 1fa47e53..96780361 100644 --- a/ghy-system/src/main/java/com/ghy/system/service/impl/SysDeptConfigServiceImpl.java +++ b/ghy-system/src/main/java/com/ghy/system/service/impl/SysDeptConfigServiceImpl.java @@ -10,6 +10,7 @@ import com.ghy.system.service.ISysDeptConfigService; import org.springframework.stereotype.Service; import javax.annotation.Resource; +import java.util.List; /** * @author clunt @@ -28,12 +29,17 @@ public class SysDeptConfigServiceImpl implements ISysDeptConfigService { public SysDeptConfig selectByDeptId(Long deptId) { SysDept dept = sysDeptMapper.selectDeptById(deptId); - if(StringUtils.isNotNull(dept) && StringUtils.isNotEmpty(dept.getAncestors())){ - Long targetDeptId = Long.parseLong(dept.getAncestors().split(",")[1]); + if (StringUtils.isNotNull(dept) && StringUtils.isNotEmpty(dept.getAncestors())) { + Long targetDeptId = Long.parseLong(dept.getAncestors().split(",")[1]); return sysDeptConfigMapper.selectByDeptId(targetDeptId); - }else { + } else { throw new ServiceException("该用户的部门ID数据有误!"); } } + + @Override + public List selectAllMerchant() { + return sysDeptConfigMapper.selectAllMerchant(); + } } diff --git a/ghy-system/src/main/resources/mapper/system/SysDeptConfigMapper.xml b/ghy-system/src/main/resources/mapper/system/SysDeptConfigMapper.xml index ede5e9f2..3c4b139b 100644 --- a/ghy-system/src/main/resources/mapper/system/SysDeptConfigMapper.xml +++ b/ghy-system/src/main/resources/mapper/system/SysDeptConfigMapper.xml @@ -4,23 +4,33 @@ "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> - - - - - - - - + + + + + + + + + + + + + + + + - SELECT sys_dept_config_id, dept_id, banner_url, create_by, create_time, remark + SELECT sys_dept_config_id, dept_id, banner_url, adapay_app_id, adapay_api_key, adapay_mock_key, + adapay_rsa_private_key, adapay_max_retry_times, wx_app_id, wx_secret, take_rate, + create_by, create_time, update_by, update_time, remark FROM sys_dept_config + + \ No newline at end of file