diff --git a/mallinkAdmin/src/main/java/com/iformall/controller/sys/UploadController.java b/mallinkAdmin/src/main/java/com/iformall/controller/sys/UploadController.java index 77b59c2af..77e33f176 100644 --- a/mallinkAdmin/src/main/java/com/iformall/controller/sys/UploadController.java +++ b/mallinkAdmin/src/main/java/com/iformall/controller/sys/UploadController.java @@ -52,7 +52,6 @@ public class UploadController extends BaseController { if (dot >= 0) { fileFormat = multiReq.getOriginalFilename().substring(dot, multiReq.getOriginalFilename().length()); } - ResultData data = aliyunOSS.uploadFile(tenantEntity.getTenantId(), fileFormat, multiReq.getInputStream()); return data; diff --git a/mallinkAdmin/src/main/resources/db/migration/V202104150001__alipay.sql b/mallinkAdmin/src/main/resources/db/migration/V202104150001__alipay.sql new file mode 100644 index 000000000..eb4169a85 --- /dev/null +++ b/mallinkAdmin/src/main/resources/db/migration/V202104150001__alipay.sql @@ -0,0 +1,40 @@ +ALTER TABLE `wx_mall` +ADD COLUMN `cash_out_support` INT(1) COMMENT '是否支持商户提现(不开启分账才有效) 0-不支持 1-支持' AFTER `live_support`; + +UPDATE wx_mall SET cash_out_support = 0 ; + +ALTER TABLE wx_merchant ADD COLUMN `cash_out_number` INT(11) DEFAULT 0 COMMENT '商户待提现金额(分)' ; + +###页面添加工作流,提现审批,22, 初始化里面也加上 + +CREATE TABLE `wx_cash_out` ( + `id` bigint(20) NOT NULL, + `total_fee` int(11) NOT NULL COMMENT '总金额(分)', + `status` tinyint(1) NOT NULL COMMENT '状态 0-提现中,1-提现成功,2-提现失败,3-提现拒绝', + `create_date` datetime DEFAULT NULL COMMENT '创建时间', + `update_date` datetime DEFAULT NULL COMMENT '更新时间', + `audio_remark` varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '审核备注', + `tenant_id` varchar(5) COLLATE utf8mb4_unicode_ci NOT NULL, + `parent_tenant_id` varchar(5) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `b_user_id` bigint(20) NOT NULL COMMENT 'merchant_b_user编号', + `wx_b_open_id` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'b端微信openId', + `audio_date` datetime DEFAULT NULL COMMENT '审批时间', + `merchant_id` bigint(20) NOT NULL COMMENT '提现商户', + `merchant_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '商户名称', + `recive_open_id` varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '接收的openId', + `recive_nick_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '昵称', + `wx_pay_no` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '支付渠道支付编号', + `wx_pay_time` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '支付渠道支付时间', + `fail_remark` varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '失败原因', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +ALTER TABLE `wx_mall` +ADD COLUMN `cash_out_limit` INT(3) COMMENT '商户提现每日次数限制' AFTER `cash_out_support`; + +UPDATE wx_mall SET cash_out_limit = 10 ; + +ALTER TABLE wx_merchant ADD COLUMN `cash_out_count` INT(3) DEFAULT 0 COMMENT '当日提现次数' ; + + + diff --git a/mallinkCApi/src/main/java/com/iformall/controller/AliPayController.java b/mallinkCApi/src/main/java/com/iformall/controller/AliPayController.java new file mode 100644 index 000000000..974eeb96e --- /dev/null +++ b/mallinkCApi/src/main/java/com/iformall/controller/AliPayController.java @@ -0,0 +1,82 @@ +package com.iformall.controller; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; +import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; +import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; +import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.iformall.annotation.AuthIgnore; +import com.iformall.annotation.TenantIgnore; +import com.iformall.common.ErrorCode; +import com.iformall.common.Result; +import com.iformall.common.ResultData; +import com.iformall.domain.po.*; +import com.iformall.domain.po.base.TenantEntity; +import com.iformall.domain.vo.WxCUserVo; +import com.iformall.domain.vo.WxLevelMerchantCVo; +import com.iformall.enums.*; +import com.iformall.service.*; +import com.iformall.service.wechat.FmOpenService; +import com.iformall.utils.Constant; +import com.iformall.utils.IPUtil; +import com.iformall.utils.RedisCacheUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import me.chanjar.weixin.common.error.WxErrorException; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.math.BigDecimal; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/api/alipay") +@Api(description = "支付宝相关接口,包括支付宝用户端操作回调接口") +public class AliPayController extends BaseController { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Autowired + private WxCUserService wxCUserService; + + + + + /** + * 支付宝客户端回调接口 + * 1. 商圈授权给isv后的回调。 isv账号登陆开放平台(https://openhome.alipay.com),选择平台应用,菜单“商家授权应用-发起授权”,复制链接或二维码给商圈支付宝账号扫。 + * 2.用户在同意领取支付宝会员卡之后的回调。 + * @param response + * @param request + * @throws IOException + */ + @AuthIgnore + @RequestMapping(value = "/callback") + @ResponseBody + public void signature(HttpServletResponse response, HttpServletRequest request) throws IOException { + + + String source = request.getParameter("source"); + //商家授权给ISV后的回调 + if ("alipay_app_auth".equals(source)) { + PrintWriter out = response.getWriter(); + out.print("授权成功!"); + out.close(); + } + + + } +} diff --git a/mallinkCallback/src/main/java/com/iformall/controller/callback/AliPayController.java b/mallinkCallback/src/main/java/com/iformall/controller/callback/AliPayController.java new file mode 100644 index 000000000..ee9162933 --- /dev/null +++ b/mallinkCallback/src/main/java/com/iformall/controller/callback/AliPayController.java @@ -0,0 +1,75 @@ +package com.iformall.controller.callback; + +import com.alibaba.fastjson.JSON; +import com.iformall.controller.base.BaseController; +import com.iformall.domain.po.msg.FmInsideNotifyPaySuccessMsg; +import com.iformall.domain.po.msg.FmInsideNotifyRefundSuccessMsg; +import com.iformall.enums.*; +import com.iformall.exception.BizMessageException; +import com.iformall.exception.MallinkException; +import com.iformall.mq.MqBaseProducer; +import com.iformall.pay.WxPayment; +import com.iformall.service.WxPayOrderService; +import com.iformall.service.WxRefundOrderService; +import com.iformall.service.WxSubsidyService; +import com.iformall.utils.XmlUtil; +import org.apache.commons.io.IOUtils; +import org.jdom2.JDOMException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +@RestController +@RequestMapping("/alipay/notify") +public class AliPayController extends BaseController { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Autowired + private WxPayOrderService wxPayOrderService; + + @Autowired + private WxSubsidyService wxSubsidyService; + + @Autowired + private MqBaseProducer mqBaseProducer; + + + + + /** + *ISV_GATEWAY_URL?charset=GBK&biz_content= + { + "trade_no":"144534352534536","out_trade_no":"123456789","total_amount":"25.00","buyer_pay_amount":"16.50","receipt_amount":"20.00","gmt_payment":"2018-05-05 14:24:12","buyer_id":"2088102121935776","mall_cell_type":"SMID","mall_cell_id":"12345","mall_pid":"2088102121935788","mall_id":"1234567","mall_name":"测试商圈名称","mall_store_id":"001","notify_receive_pid":"2088102121935799" + } + &msg_method=alipay.business.mall.trade.success&utc_timestamp=1516797622752&version=1.1&sign_type=RSA2¬ify_id=d275fec564e62af6bedbcee73f3f05fi5x&app_id=2013121700999429&sign=I+Y/lvqYUEEc10EPdpntRhFIQ== + + + ISV_GATEWAY_URL?charset=GBK&biz_content= + { + "trade_no":"2019082222001464620500962620","out_trade_no":"out202002240001","out_request_no":"outrequest0001","refund_fee":"20.00","gmt_refund_pay":"2020-01-05 14:24:12","buyer_id":"2088102121935776","mall_cell_type":"SMID","mall_cell_id":"12345","mall_pid":"2088102121935788","mall_id":"1234567","mall_store_id":"001","mall_name":"测试商圈名称","notify_receive_pid":"2088102121935799" + } + &msg_method=alipay.business.mall.trade.refunded&utc_timestamp=1516797622752&version=1.1&sign_type=RSA2¬ify_id=d275fec564e62af6bedbcee73f3f05fi5x&app_id=2013121700999429&sign=I+Y/lvqYUEEc10EPdpntRhFIQ== + * @throws Exception 可能产生的任何异常 + */ + @PostMapping(value = "/") + public void __Notify(@RequestBody Map paramMap) throws Exception { + logger.info(">>>>>>>>alipay callback"+paramMap); + } + +} diff --git a/mallinkSchedule/src/main/java/com/iformall/schedule/CashOutSchedule.java b/mallinkSchedule/src/main/java/com/iformall/schedule/CashOutSchedule.java new file mode 100644 index 000000000..cea0ffcdf --- /dev/null +++ b/mallinkSchedule/src/main/java/com/iformall/schedule/CashOutSchedule.java @@ -0,0 +1,90 @@ +package com.iformall.schedule; + +import com.iformall.domain.po.WxCashOut; +import com.iformall.domain.po.WxCouponChannel; +import com.iformall.domain.po.WxCouponOrder; +import com.iformall.domain.po.WxGame; +import com.iformall.domain.po.WxMall; +import com.iformall.domain.po.WxMerchant; +import com.iformall.enums.EnumCashOutStatus; +import com.iformall.enums.EnumCouponChannelStatus; +import com.iformall.enums.EnumCouponChannelType; +import com.iformall.enums.EnumGameStatus; +import com.iformall.mapper.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +@Component +public class CashOutSchedule { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Autowired + private WxCashOutMapper wxCashOutMapper; + @Autowired + private WxMallMapper wxMallMapper; + @Autowired + private WxMerchantMapper wxMerchantMapper; + + + private List getMalls() { + WxMall wxMall = new WxMall(); + return wxMallMapper.findList(wxMall); + } + + @Scheduled(cron = "0/5 * * * * ?") // 执行一次 + public void cashOutFailSchedule() { + List malls = getMalls(); + for (WxMall mall : malls) { + try { + WxCashOut cashOut = new WxCashOut(); + cashOut.updateTenantInfo(mall); + List statausLis = new ArrayList(); + statausLis.add(EnumCashOutStatus.FAIL.getCode()); + statausLis.add(EnumCashOutStatus.DOING.getCode()); + cashOut.setStatusList(statausLis); + + Date curDate = new Date(); + //时间在1天以上的 + Calendar startA = Calendar.getInstance(); + startA.setTime(curDate); + startA.add(Calendar.DAY_OF_YEAR, -1); + cashOut.setCreateDateEnd(startA.getTime()); + List cashoutlist = wxCashOutMapper.findList(cashOut); + if (null != cashoutlist && cashoutlist.size() > 0 ) { + for (WxCashOut casho : cashoutlist) { + cashOutBack(casho); + } + } + + + }catch(Exception e) { + logger.error("couponExpiringSchedule error.mall:"+mall.getTenantId(),e); + } + } + } + + @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class}) + public void cashOutBack(WxCashOut cashout) { + //状态更新 + cashout.setStatus(EnumCashOutStatus.BACK.getCode()); + cashout.setUpdateDate(new Date()); + wxCashOutMapper.updateStatus(cashout); + + //余额回滚 + WxMerchant merchant = wxMerchantMapper.selectById(cashout.getMerchantId()); + merchant.setCashOutNumber(cashout.getTotalFee()); + wxMerchantMapper.increaseCash(merchant); + } + +} \ No newline at end of file diff --git a/mallinkService/pom.xml b/mallinkService/pom.xml index 8a019e55a..6821563c8 100644 --- a/mallinkService/pom.xml +++ b/mallinkService/pom.xml @@ -34,7 +34,12 @@ com.iformall mybatis-multi-tenancy 1.0 - + + + com.alipay.sdk + alipay-easysdk + 2.2.0 + \ No newline at end of file diff --git a/mallinkService/src/main/java/com/iformall/domain/po/WxCashOut.java b/mallinkService/src/main/java/com/iformall/domain/po/WxCashOut.java index 2b5a4f5c6..dc15cb681 100644 --- a/mallinkService/src/main/java/com/iformall/domain/po/WxCashOut.java +++ b/mallinkService/src/main/java/com/iformall/domain/po/WxCashOut.java @@ -1,5 +1,6 @@ package com.iformall.domain.po; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import com.iformall.domain.po.base.TenantEntity; import lombok.Data; @@ -7,6 +8,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import java.util.Date; +import java.util.List; @TableName(value = "wx_cash_out") @Data @@ -20,8 +22,14 @@ public class WxCashOut extends TenantEntity { private Integer totalFee; @io.swagger.annotations.ApiModelProperty(value = "状态 0-提现中,1-提现成功,2-提现失败,3-提现拒绝", name = "status") private Integer status; + @TableField(exist = false) + private List statusList; @io.swagger.annotations.ApiModelProperty(value = "创建时间", name = "createDate") private Date createDate; + @TableField(exist = false) + private Date createDateBegin; + @TableField(exist = false) + private Date createDateEnd; @io.swagger.annotations.ApiModelProperty(value = "更新时间", name = "updateDate") private Date updateDate; @io.swagger.annotations.ApiModelProperty(value = "审核备注", name = "audioRemark") diff --git a/mallinkService/src/main/java/com/iformall/enums/EnumCashOutStatus.java b/mallinkService/src/main/java/com/iformall/enums/EnumCashOutStatus.java index aa894e83a..143ed3e87 100644 --- a/mallinkService/src/main/java/com/iformall/enums/EnumCashOutStatus.java +++ b/mallinkService/src/main/java/com/iformall/enums/EnumCashOutStatus.java @@ -5,12 +5,13 @@ package com.iformall.enums; */ public enum EnumCashOutStatus { //状态 0-提现中,1-提现成功,2-提现失败,3-提现拒绝 - DOING(0, "提现中"), + DOING(0, "发起提现"), SUCCESS(1, "提现成功"), - FAIL(2, "提现失败"), + FAIL(2, "提现失败,待退回余额"), REJECT(3, "提现拒绝"), AUDIOING(4, "审批中"), - WAITING(5, "待到账") + WAITING(5, "审批通过,待到账"), + BACK(6,"提现失败,已回退余额"); ; public static EnumCashOutStatus getEnum(Integer code) { diff --git a/mallinkService/src/main/java/com/iformall/service/impl/WxCashOutServiceImpl.java b/mallinkService/src/main/java/com/iformall/service/impl/WxCashOutServiceImpl.java index e5e868dc2..8497f1f38 100644 --- a/mallinkService/src/main/java/com/iformall/service/impl/WxCashOutServiceImpl.java +++ b/mallinkService/src/main/java/com/iformall/service/impl/WxCashOutServiceImpl.java @@ -257,65 +257,65 @@ public class WxCashOutServiceImpl implements WxCashOutService { public ResultData reCashout(WxMerchantBUser buser, WxCashOut record, EnumPayWay payWay) { //不能重复提交操作 - long time = System.currentTimeMillis() + RedisLock.LONG_TIMEOUT; - String timeStr = String.valueOf(time); - boolean cashsetlock = redisLock.lock("merchantLockReCashSet_"+record.getId(), timeStr); - if (cashsetlock) { - try { - WxCashOut cashout = this.getById(record.getId(), buser.getTenantId()); - if (null == cashout ) { - return new ResultData(Result.ERROR,"该提现记录不存在"); - } - - if( EnumCashOutStatus.FAIL.getCode().intValue() != record.getStatus().intValue()) { - return new ResultData(Result.ERROR,"提现失败状态才能重新提现。"); - } - - //只有商户管理员才能进行此操作 - WxMerchant merchant = wxMerchantMapper.selectById(buser.getMerchantId()); - if (!(buser.getPhone().equals(merchant.getLinkPhone()))) { - return new ResultData(Result.ERROR,"当前账号非商户管理员,不能进行此操作。"); - } - - WxMall mall = wxMallMapper.getByTenantId(buser.getTenantId()); - if(!hasCashCount(mall,merchant)) { - return new ResultData(Result.ERROR,"当日提现次数已达限额。"); - } - - WxAppinfo appInfo = wxAppinfoService.getCAppInfo(buser, payWay); - if (null == appInfo) { - return new ResultData(Result.ERROR,"系统错误,appInfo未查询到。"); - } - WxPayAccount payAccount = wxPayAccountService.getByTenantId(buser.getTenantId()); - if (null == payAccount) { - return new ResultData(Result.ERROR,"系统错误,payAccount未查询到。"); - } - //查询是否已经真实的转过了 - CashOutAdapterResult qresult = payServiceFactory.getCashOutAdapterService(payWay.getCode()).queryCashOut(appInfo, payAccount, cashout); - if (qresult.isSuccess()) { - cashout.setStatus(EnumCashOutStatus.SUCCESS.getCode()); - wxCashOutMapper.updateStatus(cashout); - }else { - CashOutAdapterResult result = payServiceFactory.getCashOutAdapterService(payWay.getCode()).cashOut(appInfo, payAccount, cashout); - cashout.setUpdateDate(new Date()); - if (result.isSuccess()) { - cashout.setStatus(EnumCashOutStatus.SUCCESS.getCode()); - }else { - cashout.setStatus(EnumCashOutStatus.FAIL.getCode()); - cashout.setFailRemark(result.getMsg()); - } - wxCashOutMapper.updateById(cashout); - } - //更新次数 - wxMerchantMapper.increaseCashCount(merchant); - redisLock.unlock("merchantLockReCashSet_"+record.getId(), timeStr); - }catch(Exception e) { - redisLock.unlock("merchantLockReCashSet_"+record.getId(), timeStr); - logger.error("update cashout fail.",e); - }finally { - redisLock.unlock("merchantLockReCashSet_"+record.getId(), timeStr); - } - } +// long time = System.currentTimeMillis() + RedisLock.LONG_TIMEOUT; +// String timeStr = String.valueOf(time); +// boolean cashsetlock = redisLock.lock("merchantLockReCashSet_"+record.getId(), timeStr); +// if (cashsetlock) { +// try { +// WxCashOut cashout = this.getById(record.getId(), buser.getTenantId()); +// if (null == cashout ) { +// return new ResultData(Result.ERROR,"该提现记录不存在"); +// } +// +// if( EnumCashOutStatus.FAIL.getCode().intValue() != record.getStatus().intValue()) { +// return new ResultData(Result.ERROR,"提现失败状态才能重新提现。"); +// } +// +// //只有商户管理员才能进行此操作 +// WxMerchant merchant = wxMerchantMapper.selectById(buser.getMerchantId()); +// if (!(buser.getPhone().equals(merchant.getLinkPhone()))) { +// return new ResultData(Result.ERROR,"当前账号非商户管理员,不能进行此操作。"); +// } +// +// WxMall mall = wxMallMapper.getByTenantId(buser.getTenantId()); +// if(!hasCashCount(mall,merchant)) { +// return new ResultData(Result.ERROR,"当日提现次数已达限额。"); +// } +// +// WxAppinfo appInfo = wxAppinfoService.getCAppInfo(buser, payWay); +// if (null == appInfo) { +// return new ResultData(Result.ERROR,"系统错误,appInfo未查询到。"); +// } +// WxPayAccount payAccount = wxPayAccountService.getByTenantId(buser.getTenantId()); +// if (null == payAccount) { +// return new ResultData(Result.ERROR,"系统错误,payAccount未查询到。"); +// } +// //查询是否已经真实的转过了 +// CashOutAdapterResult qresult = payServiceFactory.getCashOutAdapterService(payWay.getCode()).queryCashOut(appInfo, payAccount, cashout); +// if (qresult.isSuccess()) { +// cashout.setStatus(EnumCashOutStatus.SUCCESS.getCode()); +// wxCashOutMapper.updateStatus(cashout); +// }else { +// CashOutAdapterResult result = payServiceFactory.getCashOutAdapterService(payWay.getCode()).cashOut(appInfo, payAccount, cashout); +// cashout.setUpdateDate(new Date()); +// if (result.isSuccess()) { +// cashout.setStatus(EnumCashOutStatus.SUCCESS.getCode()); +// }else { +// cashout.setStatus(EnumCashOutStatus.FAIL.getCode()); +// cashout.setFailRemark(result.getMsg()); +// } +// wxCashOutMapper.updateById(cashout); +// } +// //更新次数 +// wxMerchantMapper.increaseCashCount(merchant); +// redisLock.unlock("merchantLockReCashSet_"+record.getId(), timeStr); +// }catch(Exception e) { +// redisLock.unlock("merchantLockReCashSet_"+record.getId(), timeStr); +// logger.error("update cashout fail.",e); +// }finally { +// redisLock.unlock("merchantLockReCashSet_"+record.getId(), timeStr); +// } +// } return new ResultData(); } } diff --git a/mallinkService/src/main/java/com/iformall/service/pay/alipay/AliPayUtil.java b/mallinkService/src/main/java/com/iformall/service/pay/alipay/AliPayUtil.java new file mode 100644 index 000000000..b85c0217e --- /dev/null +++ b/mallinkService/src/main/java/com/iformall/service/pay/alipay/AliPayUtil.java @@ -0,0 +1,159 @@ +package com.iformall.service.pay.alipay; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang3.StringUtils; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alipay.easysdk.base.oauth.models.AlipaySystemOauthTokenResponse; +import com.alipay.easysdk.util.generic.models.AlipayOpenApiGenericResponse; +import com.iformall.service.pay.alipay.api.AliPayApi; +import com.iformall.service.pay.alipay.api.entity.memberCardModel.AlipayMemberCardModelColumn; +import com.iformall.service.pay.alipay.api.entity.memberCardModel.AlipayMemberCardModelFieldRule; +import com.iformall.service.pay.alipay.api.entity.memberCardModel.AlipayMemberCardModelRequest; +import com.iformall.service.pay.alipay.api.entity.memberCardModel.AlipayMemberCardModelStyle; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class AliPayUtil { + + //查询app_auth_token + public static String getAppAuthToken(String appAuthCode) { + try { + AlipaySystemOauthTokenResponse response = AliPayApi.getAppAuthToken(appAuthCode); + return response.getAccessToken(); + } catch (Exception e) { + log.error("alipay getAppAuthToken error. ",e); + } + return null; + } + + private static JSONObject getGenericResponse(String result,String key) { + if (StringUtils.isBlank(result)) { + log.error("alipay apiresponse ["+key+"] error. no result."+result); + return null; + } + JSONObject object = JSON.parseObject(result); + if (null != object) { + JSONObject resultObject = object.getJSONObject(key); + if (null != resultObject ) { + String code = resultObject.getString("code"); + if (("10000".equals(code))) { + return resultObject; + } + } + } + log.error("alipay apiresponse ["+key+"] error. "+result); + return null; + } + + //门店照片上传 + public static String merchantImageUpload(String appAuthToken,String imageName,byte[] file) { + try { + AlipayOpenApiGenericResponse response = AliPayApi.uploadMerchantImage(appAuthToken,imageName, file); + JSONObject result = getGenericResponse(response.getHttpBody(),"alipay_offline_material_image_upload_response"); + if (null != result) { + return result.getString("image_id"); + } + } catch (Exception e) { + log.error("alipay merchantImageUpload error. ",e); + } + return null; + } + + //创建商圈会员卡 + public static String createSmartDistrictMemberCardModel(String appAuthToken,String cardName,String logoId,String backImageId) { + AlipayMemberCardModelRequest request = new AlipayMemberCardModelRequest(); + AlipayMemberCardModelStyle style = new AlipayMemberCardModelStyle(); + style.setCard_show_name(cardName); + style.setLogo_id(logoId); + style.setBackground_id(backImageId); + request.setTemplate_style_info(style); + + List columnList = new ArrayList(); + AlipayMemberCardModelColumn column0 = new AlipayMemberCardModelColumn(); + column0.setCode("TELEPHONE"); + column0.setTitle("联系电话"); + columnList.add(column0); + AlipayMemberCardModelColumn column1 = new AlipayMemberCardModelColumn(); + column1.setCode("BENEFIT_INFO"); + column1.setTitle("会员专享"); + columnList.add(column1); + request.setColumn_info_list(columnList); + + List ruleList = new ArrayList(); + AlipayMemberCardModelFieldRule rule0 = new AlipayMemberCardModelFieldRule(); + rule0.setField_name("Balance"); + rule0.setRule_name("ASSIGN_FROM_REQUEST"); + rule0.setRule_value("Balance"); + ruleList.add(rule0); + + try { + AlipayOpenApiGenericResponse response = AliPayApi.createMemberCardModel(appAuthToken, request); + JSONObject result = getGenericResponse(response.getHttpBody(),"alipay_marketing_card_template_create_response"); + if (null != result) { + return result.getString("template_id"); + } + } catch (Exception e) { + log.error("alipay createSmartDistrictMemberCardModel error. ",e); + } + return null; + } + + //创建商圈会员卡表单配置 + public static boolean setSmartDistrictMemberCardModelConfig(String appAuthToken,String templateId) { + try { + AlipayOpenApiGenericResponse response = AliPayApi.setMemberCardModelConfig(appAuthToken, templateId, + new String[] {"OPEN_FORM_FIELD_MOBILE","OPEN_FORM_FIELD_NAME"},new String[] {"OPEN_FORM_FIELD_GENDER"}); + JSONObject result = getGenericResponse(response.getHttpBody(),"alipay_marketing_card_formtemplate_set_response"); + if (null != result) { + return true; + } + } catch (Exception e) { + log.error("alipay setSmartDistrictMemberCardModelConfig error. ",e); + } + return false; + } + + //获取商圈会员卡领卡投放链接 + public static String getSmartDistrictMemberCardUrl(String appAuthToken,String templateId,String callback,String param) { + try { + AlipayOpenApiGenericResponse response = AliPayApi.getMemberCardUrl(appAuthToken, templateId, param, callback); + JSONObject result = getGenericResponse(response.getHttpBody(),"alipay_marketing_card_activateurl_apply_response"); + if (null != result) { + return result.getString("apply_card_url"); + } + } catch (Exception e) { + log.error("alipay getSmartDistrictMemberCardUrl error. ",e); + } + return null; + } + + //商圈智能积分授权(算法授权)URL + public static String getH5SmartDistrictMallVipPointsUrl(String appId,String callback,String param) { + try { + return "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?app_id=APPID&scope=mall_vip_points&redirect_uri="+URLEncoder.encode(callback,"utf-8")+"&state="+param; + } catch (UnsupportedEncodingException e) { + log.error("alipay getH5MallVipPointsUrl error. ",e); + } + return null; + } + + //商圈消息订阅 + public static boolean smartDistrictTopicSubscribe(String appAuthToken) { + try { + AlipayOpenApiGenericResponse response = AliPayApi.topicSubscribe(appAuthToken, "app_auth", "alipay.open.auth.appauth.cancelled", "HTTP", "BIZ_TAG"); + JSONObject result = getGenericResponse(response.getHttpBody(),"alipay_open_app_message_topic_subscribe_response"); + if (null != result) { + return true; + } + } catch (Exception e) { + log.error("alipay smartDistrictTopicSubscribe error. ",e); + } + return false; + } + +} diff --git a/mallinkService/src/main/java/com/iformall/service/pay/alipay/api/AliPayApi.java b/mallinkService/src/main/java/com/iformall/service/pay/alipay/api/AliPayApi.java new file mode 100644 index 000000000..8605887dd --- /dev/null +++ b/mallinkService/src/main/java/com/iformall/service/pay/alipay/api/AliPayApi.java @@ -0,0 +1,166 @@ +package com.iformall.service.pay.alipay.api; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.apache.commons.lang3.StringUtils; + +import com.alibaba.fastjson.JSON; +import com.alipay.easysdk.base.image.models.AlipayOfflineMaterialImageUploadResponse; +import com.alipay.easysdk.base.oauth.models.AlipaySystemOauthTokenResponse; +import com.alipay.easysdk.factory.Factory; +import com.alipay.easysdk.util.generic.models.AlipayOpenApiGenericResponse; +import com.iformall.service.pay.alipay.api.entity.memberCardModel.AlipayMemberCardModelColumn; +import com.iformall.service.pay.alipay.api.entity.memberCardModel.AlipayMemberCardModelFieldRule; +import com.iformall.service.pay.alipay.api.entity.memberCardModel.AlipayMemberCardModelRequest; +import com.iformall.service.pay.alipay.api.entity.memberCardModel.AlipayMemberCardModelStyle; + +/** + * SDK说明 https://opendocs.alipay.com/open/54/00y8k9 + * 综合体支付积分https://opendocs.alipay.com/open/01lsmc?scene=SC00001296 + * @author alascor + */ +public class AliPayApi { + + /** + * https://opendocs.alipay.com/apis/api_9/alipay.open.auth.token.app + * 换取应用授权令牌 + * @throws Exception + */ + public static AlipaySystemOauthTokenResponse getAppAuthToken(String appAuthCode) throws Exception { + AlipaySystemOauthTokenResponse response = Factory.Base.OAuth().getToken(appAuthCode); + return response; + } + + /** + * https://opendocs.alipay.com/apis/api_3/alipay.offline.material.image.upload + * 上传门店照片和视频接口 + * @throws Exception + */ + public static AlipayOfflineMaterialImageUploadResponse uploadMerchantImage(String appAuthToken,String imageName,String localImagePath) throws Exception { + AlipayOfflineMaterialImageUploadResponse response = Factory.Base.Image().agent(appAuthToken).upload(imageName, localImagePath); + return response; + } + + public static AlipayOpenApiGenericResponse uploadMerchantImage(String appAuthToken,String imageName,byte[] file) throws Exception { + //设置系统参数(OpenAPI中非biz_content里的参数) + Map textParams = new HashMap(); + textParams.put("app_auth_token", appAuthToken); + + //设置业务参数(OpenAPI中biz_content里的参数) + Map bizParams = new HashMap(); + bizParams.put("image_type","jpg"); + bizParams.put("image_name",imageName); + bizParams.put("image_content",file); + AlipayOpenApiGenericResponse response = Factory.Util.Generic().agent(appAuthToken) + .execute("alipay.offline.material.image.upload", textParams, bizParams); + return response; + } + + /** + * https://opendocs.alipay.com/apis/api_5/alipay.marketing.card.template.create + * 会员卡模板创建 + * @throws Exception + */ + public static AlipayOpenApiGenericResponse createMemberCardModel(String appAuthToken,AlipayMemberCardModelRequest modelRequest) throws Exception { + //设置系统参数(OpenAPI中非biz_content里的参数) + Map textParams = new HashMap(); + textParams.put("app_auth_token", appAuthToken); + + //设置业务参数(OpenAPI中biz_content里的参数) + Map bizParams = new HashMap(); + bizParams.put("request_id", UUID.randomUUID().toString()); + bizParams.put("card_type", modelRequest.getCard_type()); + bizParams.put("biz_no_suffix_len",modelRequest.getBiz_no_suffix_len()); + bizParams.put("write_off_type", modelRequest.getWrite_off_type()); + + AlipayMemberCardModelStyle style = modelRequest.getTemplate_style_info(); + bizParams.put("template_style_info", style); + + List columnList = modelRequest.getColumn_info_list(); + bizParams.put("column_info_list", columnList); + + List ruleList = modelRequest.getField_rule_list(); + bizParams.put("field_rule_list", ruleList); + + AlipayOpenApiGenericResponse response = Factory.Util.Generic().execute( + "alipay.marketing.card.template.create", textParams, bizParams); + return response; + + } + + /** + * https://opendocs.alipay.com/apis/api_5/alipay.marketing.card.formtemplate.set + * 会员卡开卡表单模板配置 + * @throws Exception + */ + public static AlipayOpenApiGenericResponse setMemberCardModelConfig(String appAuthToken,String templateId,String[] requiredFields,String[] optionalFields) throws Exception { + //设置系统参数(OpenAPI中非biz_content里的参数) + Map textParams = new HashMap(); + textParams.put("app_auth_token", appAuthToken); + + //设置业务参数(OpenAPI中biz_content里的参数) + Map bizParams = new HashMap(); + bizParams.put("template_id", templateId); + Map extendParams = new HashMap<>(); + Map requireds = new HashMap(); + requireds.put("common_fields", requiredFields); + extendParams.put("required", JSON.toJSONString(requireds)); + if (null != optionalFields) { + Map optional = new HashMap(); + optional.put("common_fields", optionalFields); + extendParams.put("optional", JSON.toJSONString(optional)); + } + bizParams.put("fields", extendParams); + + AlipayOpenApiGenericResponse response = Factory.Util.Generic().execute( + "alipay.marketing.card.formtemplate.set", textParams, bizParams); + return response; + } + + /** + * https://opendocs.alipay.com/apis/api_5/alipay.marketing.card.activateurl.apply + * 获取会员卡领卡投放链接 + */ + public static AlipayOpenApiGenericResponse getMemberCardUrl(String appAuthToken,String templateId,String exparam,String callback) throws Exception { + //设置系统参数(OpenAPI中非biz_content里的参数) + Map textParams = new HashMap(); + textParams.put("app_auth_token", appAuthToken); + + //设置业务参数(OpenAPI中biz_content里的参数) + Map bizParams = new HashMap(); + bizParams.put("template_id", templateId); + bizParams.put("out_string", exparam); + bizParams.put("callback", callback); + AlipayOpenApiGenericResponse response = Factory.Util.Generic().execute( + "alipay.marketing.card.activateurl.apply", textParams, bizParams); + return response; + } + + /** + * https://opendocs.alipay.com/apis/api_9/alipay.open.app.message.topic.subscribe + * 订阅消息主题 + * @return + * @throws Exception + */ + public static AlipayOpenApiGenericResponse topicSubscribe(String appAuthToken,String authType,String topic,String type,String tag) throws Exception { + //设置系统参数(OpenAPI中非biz_content里的参数) + Map textParams = new HashMap(); + textParams.put("app_auth_token", appAuthToken); + + //设置业务参数(OpenAPI中biz_content里的参数) + Map bizParams = new HashMap(); + bizParams.put("auth_token", appAuthToken); + bizParams.put("auth_type", authType); + bizParams.put("topic", topic); + bizParams.put("comm_type", type); + if (!StringUtils.isBlank(tag)) { + bizParams.put("tag",tag); + } + AlipayOpenApiGenericResponse response = Factory.Util.Generic().execute( + "alipay.open.app.message.topic.subscribe", textParams, bizParams); + return response; + } +} diff --git a/mallinkService/src/main/java/com/iformall/service/pay/alipay/api/entity/memberCardModel/AlipayMemberCardModelColumn.java b/mallinkService/src/main/java/com/iformall/service/pay/alipay/api/entity/memberCardModel/AlipayMemberCardModelColumn.java new file mode 100644 index 000000000..6704d7e78 --- /dev/null +++ b/mallinkService/src/main/java/com/iformall/service/pay/alipay/api/entity/memberCardModel/AlipayMemberCardModelColumn.java @@ -0,0 +1,21 @@ +package com.iformall.service.pay.alipay.api.entity.memberCardModel; + +import com.aliyun.tea.TeaModel; + +public class AlipayMemberCardModelColumn{ + + private String code;//标准栏位:行为由支付宝统一定,同时已经分配标准Code + private String title;//栏目的标题 + public String getCode() { + return code; + } + public void setCode(String code) { + this.code = code; + } + public String getTitle() { + return title; + } + public void setTitle(String title) { + this.title = title; + } +} diff --git a/mallinkService/src/main/java/com/iformall/service/pay/alipay/api/entity/memberCardModel/AlipayMemberCardModelFieldRule.java b/mallinkService/src/main/java/com/iformall/service/pay/alipay/api/entity/memberCardModel/AlipayMemberCardModelFieldRule.java new file mode 100644 index 000000000..3dd90ff7f --- /dev/null +++ b/mallinkService/src/main/java/com/iformall/service/pay/alipay/api/entity/memberCardModel/AlipayMemberCardModelFieldRule.java @@ -0,0 +1,28 @@ +package com.iformall.service.pay.alipay.api.entity.memberCardModel; + +import com.aliyun.tea.TeaModel; + +public class AlipayMemberCardModelFieldRule{ + + private String field_name;//字段名称,现在支持如下几个Key(暂不支持自定义) + private String rule_name;//规则名 + private String rule_value;//根据rule_name,采取相应取值策略 + public String getField_name() { + return field_name; + } + public void setField_name(String field_name) { + this.field_name = field_name; + } + public String getRule_name() { + return rule_name; + } + public void setRule_name(String rule_name) { + this.rule_name = rule_name; + } + public String getRule_value() { + return rule_value; + } + public void setRule_value(String rule_value) { + this.rule_value = rule_value; + } +} diff --git a/mallinkService/src/main/java/com/iformall/service/pay/alipay/api/entity/memberCardModel/AlipayMemberCardModelRequest.java b/mallinkService/src/main/java/com/iformall/service/pay/alipay/api/entity/memberCardModel/AlipayMemberCardModelRequest.java new file mode 100644 index 000000000..0ba1ed14a --- /dev/null +++ b/mallinkService/src/main/java/com/iformall/service/pay/alipay/api/entity/memberCardModel/AlipayMemberCardModelRequest.java @@ -0,0 +1,52 @@ +package com.iformall.service.pay.alipay.api.entity.memberCardModel; + +import java.util.List; + +import com.aliyun.tea.NameInMap; +import com.aliyun.tea.Validation; + +public class AlipayMemberCardModelRequest{ + + private String card_type="OUT_MEMBER_CARD";//卡类型。可选类型如下:OUT_MEMBER_CARD:外部权益卡 + private String biz_no_suffix_len ="8";//业务卡号后缀的长度,取值范围为[8,32] + private String write_off_type = "qrcode";//卡包详情页面中展现出的卡码 + private AlipayMemberCardModelStyle template_style_info;//模板样式信息 + private List column_info_list;//栏位信息 + private List field_rule_list;//字段规则列表,会员卡开卡过程中,会员卡信息的生成规则 + public String getCard_type() { + return card_type; + } + public void setCard_type(String card_type) { + this.card_type = card_type; + } + public String getBiz_no_suffix_len() { + return biz_no_suffix_len; + } + public void setBiz_no_suffix_len(String biz_no_suffix_len) { + this.biz_no_suffix_len = biz_no_suffix_len; + } + public String getWrite_off_type() { + return write_off_type; + } + public void setWrite_off_type(String write_off_type) { + this.write_off_type = write_off_type; + } + public AlipayMemberCardModelStyle getTemplate_style_info() { + return template_style_info; + } + public void setTemplate_style_info(AlipayMemberCardModelStyle template_style_info) { + this.template_style_info = template_style_info; + } + public List getColumn_info_list() { + return column_info_list; + } + public void setColumn_info_list(List column_info_list) { + this.column_info_list = column_info_list; + } + public List getField_rule_list() { + return field_rule_list; + } + public void setField_rule_list(List field_rule_list) { + this.field_rule_list = field_rule_list; + } +} diff --git a/mallinkService/src/main/java/com/iformall/service/pay/alipay/api/entity/memberCardModel/AlipayMemberCardModelStyle.java b/mallinkService/src/main/java/com/iformall/service/pay/alipay/api/entity/memberCardModel/AlipayMemberCardModelStyle.java new file mode 100644 index 000000000..746bb5a8b --- /dev/null +++ b/mallinkService/src/main/java/com/iformall/service/pay/alipay/api/entity/memberCardModel/AlipayMemberCardModelStyle.java @@ -0,0 +1,35 @@ +package com.iformall.service.pay.alipay.api.entity.memberCardModel; + +import com.aliyun.tea.TeaModel; + +public class AlipayMemberCardModelStyle{ + + private String card_show_name;//钱包端显示名称(字符串长度) + private String logo_id;//logo的图片ID + private String background_id;//背景图片Id + private String bg_color="rgb(55,112,179)";//字体颜色(非背景色),只影响卡详情中部信息区域字体颜色 + public String getCard_show_name() { + return card_show_name; + } + public void setCard_show_name(String card_show_name) { + this.card_show_name = card_show_name; + } + public String getLogo_id() { + return logo_id; + } + public void setLogo_id(String logo_id) { + this.logo_id = logo_id; + } + public String getBackground_id() { + return background_id; + } + public void setBackground_id(String background_id) { + this.background_id = background_id; + } + public String getBg_color() { + return bg_color; + } + public void setBg_color(String bg_color) { + this.bg_color = bg_color; + } +} diff --git a/mallinkService/src/main/java/com/iformall/service/pay/service/cashout/wx/WxCashOutAdapterService.java b/mallinkService/src/main/java/com/iformall/service/pay/service/cashout/wx/WxCashOutAdapterService.java index 1ced7f489..f07651b7b 100644 --- a/mallinkService/src/main/java/com/iformall/service/pay/service/cashout/wx/WxCashOutAdapterService.java +++ b/mallinkService/src/main/java/com/iformall/service/pay/service/cashout/wx/WxCashOutAdapterService.java @@ -187,7 +187,9 @@ public class WxCashOutAdapterService implements CashOutAdapterService{ " }\n" + "}"); - + /** + * https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2 + */ @Override public CashOutAdapterResult cashOut(WxAppinfo appInfo, WxPayAccount payAccount, WxCashOut cashOut) { WxCashOutP wxCashOutP = generateWxCashOutP(payAccount, appInfo, cashOut); @@ -196,7 +198,7 @@ public class WxCashOutAdapterService implements CashOutAdapterService{ signMap = BeanUtils.toStringMap(wxCashOutP); } catch (Exception e) { log.error("零钱支付命令生辰: " + e.getMessage(),e); - throw new MallinkException(ErrorCode.SYS_PARAMETER_CAST_ERROR.getCode(), "零钱支付签名异常"); + return new CashOutAdapterResult(false, EnumCashOutStatus.FAIL.getCode(), "零钱支付签名异常", null); } String signAgent = WxPayment.createSign(signMap, payAccount.getApiKey()); @@ -206,7 +208,7 @@ public class WxCashOutAdapterService implements CashOutAdapterService{ response = WxPay.transfers(signMap, payAccount.getCertPath(), payAccount.getMchId()); } catch (Exception e) { log.error("零钱支付异常: " + e.getMessage(),e); - throw new MallinkException(ErrorCode.REFUND_ORDER_ERROR.getCode(), "零钱支付异常"); + return new CashOutAdapterResult(false, EnumCashOutStatus.FAIL.getCode(), "零钱支付异常", null); } log.info("微信零钱支付:" + wxCashOutP.toString() + ", response: " + response.toString()); return getReusltFromp(response,cashOut); @@ -292,7 +294,7 @@ public class WxCashOutAdapterService implements CashOutAdapterService{ String result_no = returnMap.get("result_code"); if ("SUCCESS".equals(result_no)) { String realStatus = returnMap.get("status"); - if ("SUCCESS".equals(realStatus)) { + if ("SUCCESS".equals(realStatus) || "PROCESSING".equals(realStatus)) { return new CashOutAdapterResult(true,EnumCashOutStatus.SUCCESS.getCode(),"微信零钱支付申请成功",returnMap); }else { String msg = returnMap.get("reason"); diff --git a/mallinkService/src/main/resources/mapper/WxCashOutMapper.xml b/mallinkService/src/main/resources/mapper/WxCashOutMapper.xml index f908fec71..23db47546 100644 --- a/mallinkService/src/main/resources/mapper/WxCashOutMapper.xml +++ b/mallinkService/src/main/resources/mapper/WxCashOutMapper.xml @@ -40,11 +40,26 @@ and `parent_tenant_id` = #{parentTenantId} + + + and `create_date` >= #{createDateBegin} + + + + and `create_date` <= #{createDateEnd} + and `status` = #{status} + + and status in + + #{statusItem} + + + and id in