ghy-all/ghy-common/src/main/java/com/ghy/common/adapay/AdapayService.java

327 lines
19 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.ghy.common.adapay;
import com.alibaba.fastjson.JSONObject;
import com.ghy.common.adapay.callback.DrawCashCallback;
import com.ghy.common.adapay.callback.PayCallback;
import com.ghy.common.adapay.callback.RefundCallback;
import com.ghy.common.adapay.callback.mapping.DrawCashReplyMapping;
import com.ghy.common.adapay.callback.mapping.PayReplyMapping;
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.util.Assert;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author HH 2022/3/25
*/
@Setter
public class AdapayService {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource
private AdapayProperties adapayProperties;
/**
* 支付确认
* 适用于延时分账的场景。只有已支付完成且延时分账的Payment对象才支持调用创建支付确认对象。
* 支持一次全额或多次部分确认,多次部分确认时,当前确认金额 + 已确认金额 + 已撤销金额不能大于原支付金额。
*
* @param paymentId [必填] String(64) Adapay生成的支付对象id
* @param orderNo [必填] String(64) 请求订单号只能为英文、数字或者下划线的一种或多种组合保证在app_id下唯一
* @param confirmAmt [必填] String(14) 确认金额必须大于0保留两位小数点如0.10、100.05等。必须小于等于原支付金额-已确认金额-已撤销金额
* @return 成功时同步返回一个包含 支付确认对象的JSON https://docs.adapay.tech/api/trade.html#id54
*/
public Map<String, Object> paymentConfirm(@NotNull String paymentId, @NotNull String orderNo, @NotNull String confirmAmt) throws BaseAdaPayException {
return paymentConfirm(paymentId, orderNo, confirmAmt, null, null, null);
}
/**
* 支付确认
* 适用于延时分账的场景。只有已支付完成且延时分账的Payment对象才支持调用创建支付确认对象。
* 支持一次全额或多次部分确认,多次部分确认时,当前确认金额 + 已确认金额 + 已撤销金额不能大于原支付金额。
*
* @param paymentId [必填] String(64) Adapay生成的支付对象id
* @param orderNo [必填] String(64) 请求订单号只能为英文、数字或者下划线的一种或多种组合保证在app_id下唯一
* @param confirmAmt [必填] String(14) 确认金额必须大于0保留两位小数点如0.10、100.05等。必须小于等于原支付金额-已确认金额-已撤销金额
* @param description String(128) 附加说明
* @param feeMode String(1) 手续费收取模式O-商户手续费账户扣取手续费I-交易金额中扣取手续费值为空时默认值为I若为O时分账对象列表中不支持传入手续费承担方
* @param divMembers 分账对象信息列表一次请求最多仅支持7个分账方。json对象 形式,详见 分账对象信息列表
* @return 成功时同步返回一个包含 支付确认对象的JSON https://docs.adapay.tech/api/trade.html#id54
*/
public Map<String, Object> paymentConfirm(@NotNull String paymentId, @NotNull String orderNo, @NotNull String confirmAmt,
String description, String feeMode, List<DivMember> divMembers) throws BaseAdaPayException {
Map<String, Object> confirmParams = new HashMap<>();
confirmParams.put("payment_id", paymentId);
confirmParams.put("order_no", orderNo);
confirmParams.put("confirm_amt", confirmAmt);
confirmParams.put("div_members", divMembers);
confirmParams.put("fee_mode", feeMode);
confirmParams.put("description", description);
return PaymentConfirm.create(confirmParams);
}
/**
* 创建余额支付请求
* 商户利用该接口进行余额支付,支持同一商户下的商户-用户,用户-商户,用户-用户间的账户余额支付
*
* @param orderNo [必填]请求订单号只能为英文、数字或者下划线的一种或多种组合保证在app_id下唯一
* @param outMemberId [必填]出账用户的member_id 若为商户本身时请传入0
* @param inMemberId [必填]入账用户的member_id 若为商户本身时请传入0
* @param transAmt [必填]交易金额必须大于0人民币为元保留两位小数点如"0.10"、"100.05"等
* @param goodsTitle [必填]商品名称
* @param goodsDesc [必填]商品描述
* @param payMode 支付模式delay- 延时分账模式(值为 delay 时div_members 字段必须为空);
* 值为空时并且div_mermbers不为空时表示实时分账
* 值为空时并且div_mermbers也为空时表示不分账
* @param divMembers 分账对象信息列表最多仅支持7个分账方
* @return 成功时同步返回交易结果的 JSON
*/
public Map<String, Object> balancePay(@NotNull String orderNo, @NotNull String outMemberId, @NotNull String inMemberId,
@NotNull String transAmt, @NotNull String goodsTitle, @NotNull String goodsDesc,
String payMode, Collection<DivMember> divMembers) throws BaseAdaPayException {
Map<String, Object> balanceParam = new HashMap<>(10);
balanceParam.put("app_id", adapayProperties.getAppId());
balanceParam.put("adapay_func_code", "settle_accounts.balancePay");
balanceParam.put("order_no", orderNo);
balanceParam.put("out_member_id", outMemberId);
balanceParam.put("in_member_id", inMemberId);
balanceParam.put("trans_amt", transAmt);
balanceParam.put("goods_title", goodsTitle);
balanceParam.put("goods_desc", goodsDesc);
balanceParam.put("pay_mode", payMode);
if (!CollectionUtils.isEmpty(divMembers)) {
balanceParam.put("div_members", divMembers);
}
return AdapayCommon.requestAdapay(balanceParam);
}
/**
* 创建结算账户对象 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 !");
break;
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 MALEFEMALE为空时表示未填写
* @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);
}
/**
* 对指定商户或者商户下用户的结算账户可用余额发起主动取现操作,金额从账户中提到绑定的结算银行卡中
*
* @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);
DrawCashReplyMapping.putCallback(orderNo, callback);
return Drawcash.create(cashParam);
}
/**
* 支付宝正扫支付
*/
public Map<String, Object> alipayQrPay(PayParam payParam, PayCallback callback, AlipayExpend expend, DeviceInfo deviceInfo,
Collection<DivMember> divMembers) throws BaseAdaPayException {
return pay("alipay_qr", payParam, callback, expend, deviceInfo, divMembers);
}
/**
* 微信公众号支付
*/
public Map<String, Object> wxPubPay(PayParam payParam, PayCallback callback, WxpayExpend expend, DeviceInfo deviceInfo,
Collection<DivMember> divMembers) throws BaseAdaPayException {
return pay("wx_pub", payParam, callback, expend, deviceInfo, divMembers);
}
/**
* 微信小程序支付
*/
public Map<String, Object> wxLitePay(PayParam payParam, PayCallback callback, WxpayExpend expend, DeviceInfo deviceInfo,
Collection<DivMember> divMembers) throws BaseAdaPayException {
return pay("wx_lite", payParam, callback, expend, deviceInfo, divMembers);
}
/**
* @param payChannel [必填项]支付渠道,详见 https://docs.adapay.tech/api/appendix.html#id2
* @param payParam [必填项]支付参数
* @param callback [必填项]处理支付结果的回调接口
* @param expend 支付渠道额外参数 https://docs.adapay.tech/api/appendix.html#expend
* @param deviceInfo 前端设备信息 https://docs.adapay.tech/api/appendix.html#deviceinfo
* @param divMembers 分账对象信息列表 https://docs.adapay.tech/api/appendix.html#divmembers
* @return 同步返回一个 支付对象,详见 https://docs.adapay.tech/api/trade.html#id2
*/
public Map<String, Object> pay(String payChannel, PayParam payParam, PayCallback callback, Expend expend,
DeviceInfo deviceInfo, Collection<DivMember> divMembers) throws BaseAdaPayException {
JSONObject paymentParams = payParam.toJSONObject();
paymentParams.put("app_id", adapayProperties.getAppId());
paymentParams.put("notify_url", adapayProperties.getNotifyUrl());
paymentParams.put("pay_channel", payChannel);
paymentParams.put("div_members", divMembers);
paymentParams.put("device_info", deviceInfo);
paymentParams.put("expend", expend);
PayReplyMapping.putCallback(payParam.getOrderNo(), callback);
logger.debug("paymentParams: {}", paymentParams.toJSONString());
return Payment.create(paymentParams);
}
/**
* 发起退款
* 当您的业务需要发起退款时,可通过 Adapay 系统提供的创建 Refund对象 方法创建一个退款对象,资金会原路退回用户的支付宝或微信中。
* 支持一次全额或多次部分退款退款次数最多不超过10次。多次部分退款时当前退款金额 + 已退款金额不能大于原支付金额。
* 对于每次撤销交易Adapay 都会通过 异步消息通知 告知结果。
* 退款对象同步返回成功表示Adapay受理成功退款结果以异步通知为准。支持退款最长时间为178天
* 若返回码是“order_id_not_exists 订单记录不存在”,既超过退款期限,无法退款成功。
*
* @param callback [必填项]处理退款结果的回调接口
* @param paymentId [必填项]支付确认对象的id
* @param refundOrderNo [必填项]订单号
* @param refundAmt [必填项]退款金额若退款金额小于原交易金额则认为是部分退款必须大于0保留两位小数点如0.10、100.05等
* @return 同步返回一个 退款对象 https://docs.adapay.tech/api/trade.html#create-refund-params
*/
public Map<String, Object> refund(RefundCallback callback, String paymentId, String refundOrderNo, String refundAmt) throws BaseAdaPayException {
Assert.notNull(callback, "callback is null!");
Assert.hasText(paymentId, "paymentId is blank!");
Assert.hasText(refundOrderNo, "refundOrderNo is blank!");
Assert.hasText(refundAmt, "refundAmt is blank!");
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);
}
/**
* 支付关单
* 针对已经创建的 支付对象,您可以调用关单接口进行交易的关闭。调用此接口后,该用户订单将不再能支付成功。 对于关单功能的使用有如下规则:
* 1.存在关单记录,不能再次关单
* 2.交易时间 1分钟 内无法关单成功
* 3.正扫交易时间超过 2小时 无法关单成功
* 4.支付宝正扫接口,如果用户没有扫码,订单不能关闭成功(二维码给用户展示,如果用户没有用手机去扫码,那这笔就不能关单; 如果用户扫过了的话(无需支付成功)就可以关单了)—-微信正扫无此条限制
* 5.网银和快捷类交易都不支持关单操作
*
* @param paymentId [必填项]由 Adapay 生成的支付对象 id
* @param reason 关单描述
* @param expend 扩展域
* @return 关单的结果将通过一个 JSON 同步返回 https://docs.adapay.tech/api/trade.html#close-payment-response
*/
public Map<String, Object> close(String paymentId, String reason, String expend) throws BaseAdaPayException {
Assert.hasText(paymentId, "paymentId is blank!");
Map<String, Object> paymentParams = new HashMap<>(4);
paymentParams.put("payment_id", paymentId);
paymentParams.put("reason", reason);
paymentParams.put("expend", expend);
return Payment.close(paymentParams);
}
}