2022-03-25 19:29:11 +08:00
|
|
|
|
package com.ghy.common.adapay;
|
|
|
|
|
|
|
2022-04-01 10:19:58 +08:00
|
|
|
|
import com.ghy.common.adapay.callback.DrawCashCallback;
|
2022-03-25 19:29:11 +08:00
|
|
|
|
import com.ghy.common.adapay.callback.PayCallback;
|
2022-03-29 20:17:36 +08:00
|
|
|
|
import com.ghy.common.adapay.callback.RefundCallback;
|
2022-04-01 10:23:09 +08:00
|
|
|
|
import com.ghy.common.adapay.callback.mapping.DrawCashReplyMapping;
|
2022-03-31 10:38:34 +08:00
|
|
|
|
import com.ghy.common.adapay.callback.mapping.PayReplyMapping;
|
|
|
|
|
|
import com.ghy.common.adapay.callback.mapping.RefundReplyMapping;
|
|
|
|
|
|
import com.ghy.common.adapay.callback.model.Expend;
|
|
|
|
|
|
import com.ghy.common.adapay.callback.model.WxLiteExpend;
|
2022-03-25 19:29:11 +08:00
|
|
|
|
import com.huifu.adapay.core.exception.BaseAdaPayException;
|
2022-05-05 21:39:37 +08:00
|
|
|
|
import com.huifu.adapay.model.*;
|
2022-04-12 10:06:42 +08:00
|
|
|
|
import lombok.Setter;
|
2022-05-05 21:39:37 +08:00
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
2022-04-12 10:06:42 +08:00
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
|
|
import org.slf4j.LoggerFactory;
|
2022-04-01 10:19:58 +08:00
|
|
|
|
import org.springframework.util.Assert;
|
2022-03-25 19:29:11 +08:00
|
|
|
|
|
2022-05-05 21:39:37 +08:00
|
|
|
|
import javax.annotation.Resource;
|
|
|
|
|
|
import javax.validation.constraints.NotNull;
|
2022-03-25 19:29:11 +08:00
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @author HH 2022/3/25
|
|
|
|
|
|
*/
|
2022-04-12 10:06:42 +08:00
|
|
|
|
@Setter
|
2022-03-25 19:29:11 +08:00
|
|
|
|
public class AdapayService {
|
|
|
|
|
|
|
2022-04-12 10:06:42 +08:00
|
|
|
|
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
|
|
|
|
|
|
|
2022-05-05 21:39:37 +08:00
|
|
|
|
@Resource
|
2022-04-12 10:06:42 +08:00
|
|
|
|
private AdapayProperties adapayProperties;
|
2022-03-25 19:29:11 +08:00
|
|
|
|
|
2022-05-05 21:39:37 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 创建结算账户对象 https://docs.adapay.tech/api/trade.html#settle-account-create
|
|
|
|
|
|
* 创建结算账户对象是为一个已创建用户对象创建结算账户,用于对用户分账金额的结算,目前仅支持绑定银行卡结算账户。
|
|
|
|
|
|
* 用户创建对私结算账户时,会对银行卡号、银行卡开户姓名、身份证号三要素认证,若认证失败,则创建结算账户失败。
|
|
|
|
|
|
* 每个结算账户对象 Adapay 系统会生成一个唯一的 id,可用于查询结算账户对象,或者删除结算账户对象。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param memberId [必填]商户下的用户id,只能为英文、数字或者下划线的一种或多种组合,保证在app_id下唯一
|
|
|
|
|
|
* @param cardId [必填]银行卡号
|
|
|
|
|
|
* @param cardName [必填]银行卡对应的户名
|
|
|
|
|
|
* @param telNo [必填]手机号
|
|
|
|
|
|
* @param bankAcctType [必填]银行账户类型:1-对公;2-对私
|
|
|
|
|
|
* @param certId 证件号,银行账户类型为对私时必填
|
|
|
|
|
|
* @param bankCode 银行编码,银行账户类型对公时必填,详见附录 银行代码 https://docs.adapay.tech/api/appendix.html#id3
|
|
|
|
|
|
* @param provCode 银行账户开户银行所在省份编码 (省市编码),银行账户类型为对公时必填,省市编码详见area.json
|
|
|
|
|
|
* @param areaCode 银行账户开户银行所在地区编码(省市编码),银行账户类型为对公时必填,省市编码详见area.json
|
|
|
|
|
|
* @return 成功时同步返回一个包含 SettleAccount对象 的 JSON。
|
|
|
|
|
|
*/
|
|
|
|
|
|
public Map<String, Object> createSettleAccount(@NotNull String memberId, @NotNull String cardId, @NotNull String cardName,
|
|
|
|
|
|
@NotNull String bankAcctType, String certId, String telNo,
|
|
|
|
|
|
String bankCode, String provCode, String areaCode) throws BaseAdaPayException {
|
|
|
|
|
|
|
|
|
|
|
|
// 结算账户信息 参见结算账户信息(AccountInfo)对象 https://docs.adapay.tech/api/appendix.html#accountinfo
|
|
|
|
|
|
Map<String, Object> accountInfo = new HashMap<>(9);
|
|
|
|
|
|
|
|
|
|
|
|
switch (bankAcctType) {
|
|
|
|
|
|
case "1":
|
|
|
|
|
|
Assert.isTrue(StringUtils.isNoneBlank(bankCode, provCode, areaCode),
|
|
|
|
|
|
"[bankCode, provCode, areaCode] cannot be empty !");
|
|
|
|
|
|
case "2":
|
|
|
|
|
|
Assert.hasText(cardId, "cardId is blank !");
|
|
|
|
|
|
accountInfo.put("cert_type", "00");
|
|
|
|
|
|
accountInfo.put("cert_id", certId);
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
throw new BaseAdaPayException("Wrong bankAcctType !");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
accountInfo.put("card_id", cardId);
|
|
|
|
|
|
accountInfo.put("card_name", cardName);
|
|
|
|
|
|
accountInfo.put("tel_no", telNo);
|
|
|
|
|
|
accountInfo.put("bank_code", bankCode);
|
|
|
|
|
|
accountInfo.put("bank_acct_type", bankAcctType);
|
|
|
|
|
|
accountInfo.put("prov_code", provCode);
|
|
|
|
|
|
accountInfo.put("area_code", areaCode);
|
|
|
|
|
|
|
|
|
|
|
|
Map<String, Object> settleCountParams = new HashMap<>(4);
|
|
|
|
|
|
settleCountParams.put("member_id", memberId);
|
|
|
|
|
|
settleCountParams.put("app_id", adapayProperties.getAppId());
|
|
|
|
|
|
// 目前仅支持:bank_account(银行卡)
|
|
|
|
|
|
settleCountParams.put("channel", "bank_account");
|
|
|
|
|
|
settleCountParams.put("account_info", accountInfo);
|
|
|
|
|
|
return SettleAccount.create(settleCountParams);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 创建实名用户 https://docs.adapay.tech/api/trade.html#member-realname
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param memberId [必填]商户下的用户id,只能为英文、数字或者下划线的一种或多种组合,保证在app_id下唯一
|
|
|
|
|
|
* @param telNo [必填]用户手机号
|
|
|
|
|
|
* @param username [必填]用户姓名
|
|
|
|
|
|
* @param certId [必填]证件号
|
|
|
|
|
|
* @param location 用户地址
|
|
|
|
|
|
* @param email 用户邮箱
|
|
|
|
|
|
* @param gender MALE:男,FEMALE:女,为空时表示未填写
|
|
|
|
|
|
* @param nickname 用户昵称
|
|
|
|
|
|
* @return 成功时同步返回一个包含Member对象的JSON
|
|
|
|
|
|
*/
|
|
|
|
|
|
public Map<String, Object> createMember(String memberId, String telNo, String username, String certId,
|
|
|
|
|
|
String location, String email, String gender, String nickname) throws BaseAdaPayException {
|
|
|
|
|
|
Map<String, Object> memberParams = new HashMap<>(7);
|
|
|
|
|
|
memberParams.put("member_id", memberId);
|
|
|
|
|
|
memberParams.put("app_id", adapayProperties.getAppId());
|
|
|
|
|
|
memberParams.put("location", location);
|
|
|
|
|
|
memberParams.put("email", email);
|
|
|
|
|
|
memberParams.put("gender", gender);
|
|
|
|
|
|
memberParams.put("nickname", nickname);
|
|
|
|
|
|
memberParams.put("tel_no", telNo);
|
|
|
|
|
|
memberParams.put("user_name", username);
|
|
|
|
|
|
// 证件类型,仅支持:00-身份证
|
|
|
|
|
|
memberParams.put("cert_type", "00");
|
|
|
|
|
|
// 接口功能号
|
|
|
|
|
|
memberParams.put("adapay_func_code", "members.realname");
|
|
|
|
|
|
memberParams.put("cert_id", certId);
|
|
|
|
|
|
return AdapayCommon.requestAdapay(memberParams);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-01 10:19:58 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 对指定商户或者商户下用户的结算账户可用余额发起主动取现操作,金额从账户中提到绑定的结算银行卡中
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param callback [必填项]处理提现结果的接口
|
|
|
|
|
|
* @param orderNo [必填项]请求订单号,只能为英文、数字或者下划线的一种或多种组合,保证在app_id下唯一
|
|
|
|
|
|
* @param cashType [必填项]取现类型:T1-T+1取现;D1-D+1取现;D0-即时取现。
|
|
|
|
|
|
* @param cashAmt [必填项]取现金额,必须大于0,人民币为元,保留两位小数点,如"0.10"、"100.05"等
|
|
|
|
|
|
* @param memberId [必填项]用户对象的member_id,若是商户本身取现时,请传入0
|
|
|
|
|
|
* @param remark 备注
|
|
|
|
|
|
* @param feeMode 手续费收取模式:O-商户手续费账户扣取手续费,I-交易金额中扣取手续费;值为空时,默认值为I;
|
|
|
|
|
|
* @return https://docs.adapay.tech/api/wallet.html#cash-response
|
|
|
|
|
|
*/
|
|
|
|
|
|
public Map<String, Object> drawCash(DrawCashCallback callback, String orderNo, String cashType, String cashAmt,
|
|
|
|
|
|
String memberId, String remark, String feeMode) throws BaseAdaPayException {
|
|
|
|
|
|
Assert.notNull(callback, "callback is null!");
|
|
|
|
|
|
Assert.hasText(orderNo, "orderNo is blank!");
|
|
|
|
|
|
Assert.hasText(cashType, "cashType is blank!");
|
|
|
|
|
|
Assert.hasText(cashAmt, "cashAmt is blank!");
|
|
|
|
|
|
Assert.hasText(memberId, "memberId is blank!");
|
|
|
|
|
|
Map<String, Object> cashParam = new HashMap<>(5);
|
|
|
|
|
|
cashParam.put("order_no", orderNo);
|
|
|
|
|
|
cashParam.put("app_id", adapayProperties.getAppId());
|
|
|
|
|
|
cashParam.put("cash_type", cashType);
|
|
|
|
|
|
cashParam.put("cash_amt", cashAmt);
|
|
|
|
|
|
cashParam.put("member_id", memberId);
|
|
|
|
|
|
cashParam.put("notify_url", adapayProperties.getNotifyUrl());
|
|
|
|
|
|
cashParam.put("remark", remark);
|
|
|
|
|
|
cashParam.put("fee_mode", feeMode);
|
2022-04-01 10:23:09 +08:00
|
|
|
|
DrawCashReplyMapping.putCallback(orderNo, callback);
|
2022-04-01 10:19:58 +08:00
|
|
|
|
return Drawcash.create(cashParam);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-25 19:29:11 +08:00
|
|
|
|
/**
|
2022-03-31 10:38:34 +08:00
|
|
|
|
* 支付宝正扫支付
|
2022-03-29 20:17:36 +08:00
|
|
|
|
*/
|
2022-05-05 21:39:37 +08:00
|
|
|
|
public Map<String, Object> alipayQrPay(PayCallback callback, String orderNo, String payAmt,
|
2022-04-24 18:19:14 +08:00
|
|
|
|
String goodsTittle, String goodsDesc, String divMembers, String description) throws BaseAdaPayException {
|
2022-05-05 21:39:37 +08:00
|
|
|
|
return pay(callback, PayChannelEnum.ALIPAY_QR.getCode(), null, orderNo, payAmt, goodsTittle, goodsDesc, divMembers, description);
|
2022-03-29 20:17:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-11 15:16:21 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 微信小程序支付
|
|
|
|
|
|
*/
|
|
|
|
|
|
public Map<String, Object> wxPubPay(PayCallback callback, WxLiteExpend expend, String orderNo, String payAmt,
|
2022-04-24 18:19:14 +08:00
|
|
|
|
String goodsTittle, String goodsDesc, String divMembers, String description) throws BaseAdaPayException {
|
|
|
|
|
|
return pay(callback, PayChannelEnum.WX_PUB.getCode(), expend, orderNo, payAmt, goodsTittle, goodsDesc, divMembers, description);
|
2022-04-11 15:16:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-29 20:17:36 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 微信小程序支付
|
2022-03-31 10:38:34 +08:00
|
|
|
|
*/
|
2022-04-01 10:19:58 +08:00
|
|
|
|
public Map<String, Object> wxLitePay(PayCallback callback, WxLiteExpend expend, String orderNo, String payAmt,
|
2022-04-24 18:19:14 +08:00
|
|
|
|
String goodsTittle, String goodsDesc, String divMembers, String description) throws BaseAdaPayException {
|
|
|
|
|
|
return pay(callback, PayChannelEnum.WX_LITE.getCode(), expend, orderNo, payAmt, goodsTittle, goodsDesc, divMembers, description);
|
2022-03-31 10:38:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2022-03-29 20:17:36 +08:00
|
|
|
|
* @param callback [必填项]处理支付结果的回调接口
|
|
|
|
|
|
* @param orderNo [必填项]订单号
|
2022-03-31 10:38:34 +08:00
|
|
|
|
* @param payAmt [必填项]交易金额,必须大于0,保留两位小数点,如"0.10"、"100.05"
|
2022-03-29 20:17:36 +08:00
|
|
|
|
* @param goodsTittle [必填项]商品名称
|
|
|
|
|
|
* @param goodsDesc [必填项]商品描述信息,微信小程序和微信公众号该字段最大长度42个字符
|
2022-03-31 10:38:34 +08:00
|
|
|
|
* @param payChannel [必填项]支付渠道,详见 https://docs.adapay.tech/api/appendix.html#id2
|
|
|
|
|
|
* @param expend 支付渠道额外参数,条件可输入,详见 https://docs.adapay.tech/api/appendix.html#expend
|
2022-04-24 18:19:14 +08:00
|
|
|
|
* @param divMembers 分账对象信息列表,最多仅支持7个分账方,json 数组形式,详见 https://docs.adapay.tech/api/appendix.html#divmembers
|
2022-03-25 19:29:11 +08:00
|
|
|
|
* @param description 订单附加说明
|
2022-03-31 10:38:34 +08:00
|
|
|
|
* @return 同步返回一个 支付对象,详见 https://docs.adapay.tech/api/trade.html#id2
|
2022-03-25 19:29:11 +08:00
|
|
|
|
*/
|
2022-04-01 10:19:58 +08:00
|
|
|
|
public Map<String, Object> pay(PayCallback callback, String payChannel, Expend expend, String orderNo, String payAmt,
|
2022-04-24 18:19:14 +08:00
|
|
|
|
String goodsTittle, String goodsDesc, String divMembers, String description) throws BaseAdaPayException {
|
2022-03-29 20:17:36 +08:00
|
|
|
|
Map<String, Object> paymentParams = new HashMap<>(16);
|
2022-03-25 19:29:11 +08:00
|
|
|
|
paymentParams.put("app_id", adapayProperties.getAppId());
|
|
|
|
|
|
paymentParams.put("notify_url", adapayProperties.getNotifyUrl());
|
|
|
|
|
|
paymentParams.put("order_no", orderNo);
|
2022-03-31 10:38:34 +08:00
|
|
|
|
paymentParams.put("pay_channel", payChannel);
|
2022-03-25 19:29:11 +08:00
|
|
|
|
paymentParams.put("pay_amt", payAmt);
|
|
|
|
|
|
paymentParams.put("goods_title", goodsTittle);
|
|
|
|
|
|
paymentParams.put("goods_desc", goodsDesc);
|
2022-04-24 18:19:14 +08:00
|
|
|
|
paymentParams.put("div_members", divMembers);
|
2022-03-25 19:29:11 +08:00
|
|
|
|
paymentParams.put("description", description);
|
2022-03-31 10:38:34 +08:00
|
|
|
|
paymentParams.put("expend", expend);
|
2022-03-29 20:17:36 +08:00
|
|
|
|
PayReplyMapping.putCallback(orderNo, callback);
|
2022-05-05 21:39:37 +08:00
|
|
|
|
logger.info("paymentParams=" + paymentParams);
|
2022-03-25 19:29:11 +08:00
|
|
|
|
return Payment.create(paymentParams);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-31 10:38:34 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 发起退款
|
|
|
|
|
|
* 当您的业务需要发起退款时,可通过 Adapay 系统提供的创建 Refund对象 方法创建一个退款对象,资金会原路退回用户的支付宝或微信中。
|
|
|
|
|
|
* 支持一次全额或多次部分退款,退款次数最多不超过10次。多次部分退款时,当前退款金额 + 已退款金额不能大于原支付金额。
|
|
|
|
|
|
* 对于每次撤销交易,Adapay 都会通过 异步消息通知 告知结果。
|
|
|
|
|
|
* 退款对象同步返回成功,表示Adapay受理成功,退款结果以异步通知为准。支持退款最长时间为178天,
|
|
|
|
|
|
* 若返回码是“order_id_not_exists 订单记录不存在”,既超过退款期限,无法退款成功。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param callback [必填项]处理退款结果的回调接口
|
|
|
|
|
|
* @param paymentId [必填项]支付确认对象的id
|
|
|
|
|
|
* @param refundOrderNo [必填项]订单号
|
|
|
|
|
|
* @param refundAmt [必填项]退款金额,若退款金额小于原交易金额,则认为是部分退款,必须大于0,保留两位小数点,如0.10、100.05等
|
2022-03-31 18:47:01 +08:00
|
|
|
|
* @return 同步返回一个 退款对象 https://docs.adapay.tech/api/trade.html#create-refund-params
|
2022-03-31 10:38:34 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public Map<String, Object> refund(RefundCallback callback, String paymentId, String refundOrderNo, String refundAmt) throws BaseAdaPayException {
|
2022-04-01 10:19:58 +08:00
|
|
|
|
Assert.notNull(callback, "callback is null!");
|
|
|
|
|
|
Assert.hasText(paymentId, "paymentId is blank!");
|
|
|
|
|
|
Assert.hasText(refundOrderNo, "refundOrderNo is blank!");
|
|
|
|
|
|
Assert.hasText(refundAmt, "refundAmt is blank!");
|
2022-03-31 10:38:34 +08:00
|
|
|
|
Map<String, Object> refundParams = new HashMap<>(4);
|
|
|
|
|
|
refundParams.put("refund_amt", refundAmt);
|
|
|
|
|
|
refundParams.put("refund_order_no", refundOrderNo);
|
|
|
|
|
|
RefundReplyMapping.putCallback(paymentId, callback);
|
|
|
|
|
|
return Refund.create(paymentId, refundParams);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-29 20:17:36 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 支付关单
|
|
|
|
|
|
* 针对已经创建的 支付对象,您可以调用关单接口进行交易的关闭。调用此接口后,该用户订单将不再能支付成功。 对于关单功能的使用有如下规则:
|
|
|
|
|
|
* 1.存在关单记录,不能再次关单
|
|
|
|
|
|
* 2.交易时间 1分钟 内无法关单成功
|
|
|
|
|
|
* 3.正扫交易时间超过 2小时 无法关单成功
|
|
|
|
|
|
* 4.支付宝正扫接口,如果用户没有扫码,订单不能关闭成功(二维码给用户展示,如果用户没有用手机去扫码,那这笔就不能关单; 如果用户扫过了的话(无需支付成功)就可以关单了)—-微信正扫无此条限制
|
|
|
|
|
|
* 5.网银和快捷类交易都不支持关单操作
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param paymentId [必填项]由 Adapay 生成的支付对象 id
|
|
|
|
|
|
* @param reason 关单描述
|
|
|
|
|
|
* @param expend 扩展域
|
2022-03-31 18:47:01 +08:00
|
|
|
|
* @return 关单的结果将通过一个 JSON 同步返回 https://docs.adapay.tech/api/trade.html#close-payment-response
|
2022-03-29 20:17:36 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public Map<String, Object> close(String paymentId, String reason, String expend) throws BaseAdaPayException {
|
2022-04-01 10:19:58 +08:00
|
|
|
|
Assert.hasText(paymentId, "paymentId is blank!");
|
2022-03-29 20:17:36 +08:00
|
|
|
|
Map<String, Object> paymentParams = new HashMap<>(4);
|
|
|
|
|
|
paymentParams.put("payment_id", paymentId);
|
|
|
|
|
|
paymentParams.put("reason", reason);
|
|
|
|
|
|
paymentParams.put("expend", expend);
|
|
|
|
|
|
return Payment.close(paymentParams);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-25 19:29:11 +08:00
|
|
|
|
}
|