| @@ -37,6 +37,12 @@ | |||||
| <artifactId>commons-fileupload</artifactId> | <artifactId>commons-fileupload</artifactId> | ||||
| <version>1.3.3</version> | <version>1.3.3</version> | ||||
| </dependency> | </dependency> | ||||
| <dependency> | |||||
| <groupId>com.google.zxing</groupId> | |||||
| <artifactId>core</artifactId> | |||||
| <version>3.3.3</version> | |||||
| </dependency> | |||||
| </dependencies> | </dependencies> | ||||
| <build> | <build> | ||||
| @@ -6,6 +6,7 @@ import com.iformall.exception.MallinkException; | |||||
| import com.iformall.pay.WxPayment; | import com.iformall.pay.WxPayment; | ||||
| import com.iformall.service.WxPayOrderService; | import com.iformall.service.WxPayOrderService; | ||||
| import com.iformall.service.WxRefundOrderService; | import com.iformall.service.WxRefundOrderService; | ||||
| import com.iformall.service.WxSubsidyService; | |||||
| import com.iformall.utils.XmlUtil; | import com.iformall.utils.XmlUtil; | ||||
| import org.apache.commons.io.IOUtils; | import org.apache.commons.io.IOUtils; | ||||
| import org.jdom2.JDOMException; | import org.jdom2.JDOMException; | ||||
| @@ -35,6 +36,9 @@ public class WxPayController extends BaseController { | |||||
| @Autowired | @Autowired | ||||
| private WxRefundOrderService wxRefundOrderService; | private WxRefundOrderService wxRefundOrderService; | ||||
| @Autowired | |||||
| private WxSubsidyService wxSubsidyService; | |||||
| /** | /** | ||||
| * | * | ||||
| * @return 接收微信异步通知 | * @return 接收微信异步通知 | ||||
| @@ -176,5 +180,68 @@ public class WxPayController extends BaseController { | |||||
| return XmlUtil.getRequestXml(resultMap); | return XmlUtil.getRequestXml(resultMap); | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * | |||||
| * @return 接收微信异步通知 | |||||
| * @throws Exception 可能产生的任何异常 | |||||
| */ | |||||
| @RequestMapping(value = "/subsidyPay", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) | |||||
| @ResponseBody | |||||
| public String _subsidyPayNotify(HttpServletRequest request) throws IOException, JDOMException { | |||||
| logger.info("[" +getIpAddr() + "]微信支付回调"); | |||||
| InputStream inStream = request.getInputStream(); | |||||
| ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); | |||||
| byte[] buffer = new byte[1024]; | |||||
| int len = 0; | |||||
| while ((len = inStream.read(buffer)) != -1) { | |||||
| outSteam.write(buffer, 0, len); | |||||
| } | |||||
| String resultxml = new String(outSteam.toByteArray(), Charset.forName("UTF-8")); | |||||
| logger.info(resultxml); | |||||
| outSteam.close(); | |||||
| inStream.close(); | |||||
| Map<String, String> paramMap = null; | |||||
| try { | |||||
| paramMap = WxPayment.xmlToMap(resultxml); | |||||
| logger.info("微信支付回调, notify, param: " + paramMap.toString() ); | |||||
| String response = wxSubsidyService.notify(paramMap, EnumPayWay.PAY_WAY_WEAPP); | |||||
| logger.info("微信支付回调, notify success, req : " + resultxml + ", resp: " + response.toString()); | |||||
| return response; | |||||
| } catch (BizMessageException e) { | |||||
| if (paramMap == null) { | |||||
| logger.error("微信支付回调, order create error, e: " + e.getMessage()); | |||||
| } else { | |||||
| logger.error("微信支付回调, order create error, req: " + resultxml + ", e: " + e.getMessage()); | |||||
| } | |||||
| SortedMap resultMap = new TreeMap<>(); | |||||
| resultMap.put("return_code", "FAIL"); | |||||
| resultMap.put("return_msg", e.getMessage()); | |||||
| return XmlUtil.getRequestXml(resultMap); | |||||
| } catch (MallinkException e) { | |||||
| if (paramMap == null) { | |||||
| logger.error("微信支付回调, order create error, e: " + e.getMessage()); | |||||
| } else { | |||||
| logger.error("微信支付回调, order create error, req: " + resultxml + ", e: " + e.getMessage()); | |||||
| } | |||||
| SortedMap resultMap = new TreeMap<>(); | |||||
| resultMap.put("return_code", "FAIL"); | |||||
| resultMap.put("return_msg", e.getMessage()); | |||||
| return XmlUtil.getRequestXml(resultMap); | |||||
| } catch (Exception e) { | |||||
| if (paramMap == null) { | |||||
| logger.error("微信支付回调, order create error, e: " + e.getMessage()); | |||||
| } else { | |||||
| logger.error("微信支付回调, order create error, req: " + resultxml + ", e: " + e.getMessage()); | |||||
| } | |||||
| SortedMap resultMap = new TreeMap(); | |||||
| resultMap.put("return_code", "FAIL"); | |||||
| resultMap.put("return_msg", e.getMessage()); | |||||
| return XmlUtil.getRequestXml(resultMap); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,103 @@ | |||||
| package com.iformall.controller; | |||||
| import com.iformall.domain.po.MallUserInfo; | |||||
| import com.iformall.exception.MallinkException; | |||||
| import com.iformall.utils.PayUtil; | |||||
| import io.swagger.annotations.Api; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.web.bind.annotation.*; | |||||
| import com.github.pagehelper.PageInfo; | |||||
| import com.iformall.common.Result; | |||||
| import com.iformall.common.ResultData; | |||||
| import com.iformall.domain.po.WxSubsidy; | |||||
| import com.iformall.service.WxSubsidyService; | |||||
| import io.swagger.annotations.ApiImplicitParam; | |||||
| import io.swagger.annotations.ApiImplicitParams; | |||||
| import io.swagger.annotations.ApiOperation; | |||||
| import javax.imageio.ImageIO; | |||||
| import javax.servlet.http.HttpServletResponse; | |||||
| import java.awt.image.BufferedImage; | |||||
| import java.util.Map; | |||||
| @RestController | |||||
| @Api(description = "商城补贴支付接口") | |||||
| @RequestMapping("wxSubsidy") | |||||
| public class WxSubsidyController extends BaseController { | |||||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | |||||
| @Autowired | |||||
| private WxSubsidyService wxSubsidyService; | |||||
| @ApiOperation("补贴扫码支付发起") | |||||
| @GetMapping("prePay") | |||||
| @ApiImplicitParams({ | |||||
| @ApiImplicitParam(name = "amount", value = "金额", dataType = "int", paramType = "query", required = true)}) | |||||
| public void subsidyPrepay(Integer amount, HttpServletResponse response) throws Exception { | |||||
| String ipStr = getIpAddr(); | |||||
| logger.info("subsidyPrepay: " + ipStr + "-" + amount); | |||||
| MallUserInfo userInfo = getUser(); | |||||
| ResultData resultData = wxSubsidyService.createSubsidy(userInfo, ipStr, amount); | |||||
| if(resultData.code == 200) { | |||||
| String codeUrl = ((Map<String, String>)resultData.data).get("code_url"); | |||||
| BufferedImage image = PayUtil.getQRCodeImge(codeUrl); | |||||
| response.setContentType("image/jpeg"); | |||||
| response.setHeader("Pragma","no-cache"); | |||||
| response.setHeader("Cache-Control","no-cache"); | |||||
| response.setIntHeader("Expires",-1); | |||||
| ImageIO.write(image, "JPEG", response.getOutputStream()); | |||||
| } else { | |||||
| throw new MallinkException(resultData.code, resultData.message); | |||||
| } | |||||
| } | |||||
| @ApiOperation("分页列表接口") | |||||
| @GetMapping("list") | |||||
| @ApiImplicitParams({ | |||||
| @ApiImplicitParam(name="pageNum",value="页数",dataType="int", paramType = "query",required=true), | |||||
| @ApiImplicitParam(name="pageSize",value="每页条数",dataType="int", paramType = "query",required=true)}) | |||||
| public ResultData list(@ModelAttribute WxSubsidy wxSubsidy,Integer pageNum, Integer pageSize) { | |||||
| if (null == wxSubsidy) wxSubsidy = new WxSubsidy(); | |||||
| final PageInfo<WxSubsidy> page = wxSubsidyService.listAsPage(wxSubsidy, pageNum, pageSize); | |||||
| return new ResultData(page); | |||||
| } | |||||
| @ApiOperation("新增接口") | |||||
| @PostMapping("add") | |||||
| public ResultData add(@RequestBody WxSubsidy wxSubsidy) { | |||||
| //Assert.notNull(wxSubsidy.getName(), "角色名不能为空"); | |||||
| //Assert.isTrue(!checkUnique(sysRole.getName(), null), "重复的角色名"); | |||||
| wxSubsidyService.saveOrUpdate(wxSubsidy); | |||||
| return new ResultData(); | |||||
| } | |||||
| @ApiOperation("根据id更新接口") | |||||
| @PostMapping("update") | |||||
| public ResultData update(@RequestBody WxSubsidy wxSubsidy) { | |||||
| wxSubsidyService.saveOrUpdate(wxSubsidy); | |||||
| return new ResultData(); | |||||
| } | |||||
| @ApiOperation("根据id删除接口") | |||||
| @GetMapping("/del") | |||||
| @ApiImplicitParam(name="id",value="id",dataType="Long", paramType = "query",required=true) | |||||
| public ResultData delete(Long id) { | |||||
| wxSubsidyService.deleteById(id); | |||||
| return new ResultData(Result.SUCCESS, "删除成功", null); | |||||
| } | |||||
| @ApiOperation("根据id查询接口") | |||||
| @GetMapping("/findById") | |||||
| @ApiImplicitParam(name="id",value="id",dataType="Long", paramType = "query",required=true) | |||||
| public ResultData findById(Long id) { | |||||
| return new ResultData(Result.SUCCESS,"查询成功",wxSubsidyService.getById(id)); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,37 @@ | |||||
| package com.iformall.utils; | |||||
| import com.google.zxing.BarcodeFormat; | |||||
| import com.google.zxing.EncodeHintType; | |||||
| import com.google.zxing.MultiFormatWriter; | |||||
| import com.google.zxing.WriterException; | |||||
| import com.google.zxing.common.BitMatrix; | |||||
| import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; | |||||
| import java.awt.image.BufferedImage; | |||||
| import java.util.Hashtable; | |||||
| import java.util.Map; | |||||
| public class PayUtil { | |||||
| /** | |||||
| * 根据url生成二位图片对象 | |||||
| * | |||||
| * @param codeUrl | |||||
| * @return | |||||
| * @throws WriterException | |||||
| */ | |||||
| public static BufferedImage getQRCodeImge(String codeUrl) throws WriterException { | |||||
| Map<EncodeHintType, Object> hints = new Hashtable(); | |||||
| hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); | |||||
| hints.put(EncodeHintType.CHARACTER_SET, "UTF8"); | |||||
| int width = 256; | |||||
| BitMatrix bitMatrix = (new MultiFormatWriter()).encode(codeUrl, BarcodeFormat.QR_CODE, width, width, hints); | |||||
| BufferedImage image = new BufferedImage(width, width, 1); | |||||
| for(int x = 0; x < width; ++x) { | |||||
| for(int y = 0; y < width; ++y) { | |||||
| image.setRGB(x, y, bitMatrix.get(x, y) ? -16777216 : -1); | |||||
| } | |||||
| } | |||||
| return image; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,152 @@ | |||||
| package com.iformall.domain.po; | |||||
| import lombok.Data; | |||||
| import javax.persistence.*; | |||||
| import java.util.*; | |||||
| import java.math.*; | |||||
| import javax.persistence.Transient; | |||||
| import java.util.List; | |||||
| import javax.persistence.Id; | |||||
| import java.io.Serializable; | |||||
| @Table(name = "wx_subsidy") | |||||
| @Data | |||||
| public class WxSubsidy implements Serializable { | |||||
| private static final long serialVersionUID = 1L; | |||||
| @Id | |||||
| protected Long id; | |||||
| @Transient | |||||
| protected List<Long> ids; | |||||
| @Transient | |||||
| protected String sortColumns; | |||||
| /**租户ID*/ | |||||
| @io.swagger.annotations.ApiModelProperty(value="租户ID",name="tenantId") | |||||
| private String tenantId; | |||||
| /**操作用户ID*/ | |||||
| @io.swagger.annotations.ApiModelProperty(value="操作用户ID",name="operatorUserId") | |||||
| private Long operatorUserId; | |||||
| /**创建时间*/ | |||||
| @io.swagger.annotations.ApiModelProperty(value="创建时间",name="createTime") | |||||
| private Date createTime; | |||||
| /**更新时间*/ | |||||
| @io.swagger.annotations.ApiModelProperty(value="更新时间",name="updateTime") | |||||
| private Date updateTime; | |||||
| /**微信商户订单号/商品ID,同ID一致*/ | |||||
| @io.swagger.annotations.ApiModelProperty(value="微信商户订单号/商品ID,同ID一致",name="orderNo") | |||||
| private String orderNo; | |||||
| /**商品描述*/ | |||||
| @io.swagger.annotations.ApiModelProperty(value="商品描述",name="body") | |||||
| private String body; | |||||
| /**终端IP*/ | |||||
| @io.swagger.annotations.ApiModelProperty(value="终端IP",name="ip") | |||||
| private String ip; | |||||
| /**支付状态: 0-支付中;1-支付成功;2-支付失败*/ | |||||
| @io.swagger.annotations.ApiModelProperty(value="支付状态: 0-支付中;1-支付成功;2-支付失败",name="status") | |||||
| private Integer status; | |||||
| /**支付发起时间*/ | |||||
| @io.swagger.annotations.ApiModelProperty(value="支付发起时间",name="payTimeStart") | |||||
| private Date payTimeStart; | |||||
| /**支付结束时间*/ | |||||
| @io.swagger.annotations.ApiModelProperty(value="支付结束时间",name="payTimeEnd") | |||||
| private Date payTimeEnd; | |||||
| /**预支付交易会话标识*/ | |||||
| @io.swagger.annotations.ApiModelProperty(value="预支付交易会话标识",name="prepayId") | |||||
| private String prepayId; | |||||
| /***/ | |||||
| @io.swagger.annotations.ApiModelProperty(value="",name="codeUrl") | |||||
| private String codeUrl; | |||||
| /***/ | |||||
| @io.swagger.annotations.ApiModelProperty(value="",name="transactionId") | |||||
| private String transactionId; | |||||
| /**总金额*/ | |||||
| @io.swagger.annotations.ApiModelProperty(value="总金额",name="amount") | |||||
| private Integer amount; | |||||
| /**可分账总金额*/ | |||||
| @io.swagger.annotations.ApiModelProperty(value="可分账总金额",name="shareAmount") | |||||
| private Integer shareAmount; | |||||
| /**可分账剩余总金额*/ | |||||
| @io.swagger.annotations.ApiModelProperty(value="可分账剩余总金额",name="shareRemainAmount") | |||||
| private Integer shareRemainAmount; | |||||
| /**支付失败原因*/ | |||||
| @io.swagger.annotations.ApiModelProperty(value="支付失败原因",name="failReason") | |||||
| private String failReason; | |||||
| public static enum Field | |||||
| { | |||||
| Id_ASC("`id` ASC"),Id_DESC("`id` DESC") | |||||
| ,TenantId_ASC("`tenant_id` ASC"),TenantId_DESC("`tenant_id` DESC") | |||||
| ,OperatorUserId_ASC("`operator_user_id` ASC"),OperatorUserId_DESC("`operator_user_id` DESC") | |||||
| ,CreateTime_ASC("`create_time` ASC"),CreateTime_DESC("`create_time` DESC") | |||||
| ,UpdateTime_ASC("`update_time` ASC"),UpdateTime_DESC("`update_time` DESC") | |||||
| ,OrderNo_ASC("`order_no` ASC"),OrderNo_DESC("`order_no` DESC") | |||||
| ,Body_ASC("`body` ASC"),Body_DESC("`body` DESC") | |||||
| ,TotalFee_ASC("`total_fee` ASC"),TotalFee_DESC("`total_fee` DESC") | |||||
| ,Ip_ASC("`ip` ASC"),Ip_DESC("`ip` DESC") | |||||
| ,Status_ASC("`status` ASC"),Status_DESC("`status` DESC") | |||||
| ,PayTimeStart_ASC("`pay_time_start` ASC"),PayTimeStart_DESC("`pay_time_start` DESC") | |||||
| ,PayTimeEnd_ASC("`pay_time_end` ASC"),PayTimeEnd_DESC("`pay_time_end` DESC") | |||||
| ,PrepayId_ASC("`prepay_id` ASC"),PrepayId_DESC("`prepay_id` DESC") | |||||
| ,CodeUrl_ASC("`code_url` ASC"),CodeUrl_DESC("`code_url` DESC") | |||||
| ,TransactionId_ASC("`transaction_id` ASC"),TransactionId_DESC("`transaction_id` DESC") | |||||
| ,Amount_ASC("`amount` ASC"),Amount_DESC("`amount` DESC") | |||||
| ,ShareAmount_ASC("`share_amount` ASC"),ShareAmount_DESC("`share_amount` DESC") | |||||
| ,ShareRemainAmount_ASC("`share_remain_amount` ASC"),ShareRemainAmount_DESC("`share_remain_amount` DESC") | |||||
| ,FailReason_ASC("`fail_reason` ASC"),FailReason_DESC("`fail_reason` DESC") | |||||
| ,OpenId_ASC("`open_id` ASC"),OpenId_DESC("`open_id` DESC") | |||||
| ; | |||||
| private String value; | |||||
| Field(String value){ | |||||
| this.value = value; | |||||
| } | |||||
| public String getValue() { | |||||
| return value; | |||||
| } | |||||
| public void setCol(String value) { | |||||
| this.value = value; | |||||
| } | |||||
| @Override | |||||
| public String toString() { | |||||
| return this.getValue(); | |||||
| } | |||||
| } | |||||
| public void setSortColumns(WxSubsidy.Field... fields) | |||||
| { | |||||
| if (fields == null || fields.length == 0) { | |||||
| return; | |||||
| } | |||||
| for (int k = 0; k < fields.length; k++) { | |||||
| if (fields[k] == null) { | |||||
| return; | |||||
| } | |||||
| } | |||||
| StringBuilder sb = new StringBuilder(fields[0].toString()); | |||||
| for (int k = 1; k < fields.length; k++) { | |||||
| sb.append(","); | |||||
| sb.append(fields[k].toString()); | |||||
| } | |||||
| this.sortColumns = sb.toString(); | |||||
| } | |||||
| public void setSortColumns(String sortColumns) | |||||
| { | |||||
| if (sortColumns == null || "".equals(sortColumns.trim())) { | |||||
| return; | |||||
| } | |||||
| if (sortColumns.contains(",")) { | |||||
| String[] cols = sortColumns.split(","); | |||||
| java.util.List<Field> fList = new java.util.ArrayList(); | |||||
| for (int k = 0; k < cols.length; k++) { | |||||
| fList.add(Field.valueOf(cols[k])); | |||||
| } | |||||
| this.setSortColumns(fList.toArray(new Field[fList.size()])); | |||||
| } else { | |||||
| this.setSortColumns(Field.valueOf(sortColumns)); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,17 @@ | |||||
| package com.iformall.mapper; | |||||
| import java.util.*; | |||||
| import com.iformall.common.CommonMapper; | |||||
| import org.apache.ibatis.annotations.Param; | |||||
| import com.iformall.domain.po.WxSubsidy; | |||||
| public interface WxSubsidyMapper extends CommonMapper<WxSubsidy, Long> { | |||||
| List<WxSubsidy> findList(WxSubsidy wxSubsidy); | |||||
| } | |||||
| @@ -116,7 +116,7 @@ public class WxMicroPayOrderP { | |||||
| @Override | @Override | ||||
| public String toString() { | public String toString() { | ||||
| final StringBuilder sb = new StringBuilder("WxPayOrderP{"); | |||||
| final StringBuilder sb = new StringBuilder("WxMicroPayOrderP{"); | |||||
| sb.append("appid='").append(appid).append('\''); | sb.append("appid='").append(appid).append('\''); | ||||
| sb.append(", mch_id='").append(mch_id).append('\''); | sb.append(", mch_id='").append(mch_id).append('\''); | ||||
| sb.append(", device_info='").append(device_info).append('\''); | sb.append(", device_info='").append(device_info).append('\''); | ||||
| @@ -152,7 +152,7 @@ public class WxMicroPayOrderSP { | |||||
| @Override | @Override | ||||
| public String toString() { | public String toString() { | ||||
| final StringBuilder sb = new StringBuilder("WxPayOrderSP{"); | |||||
| final StringBuilder sb = new StringBuilder("WxMicroPayOrderSP {"); | |||||
| sb.append("appid='").append(appid).append('\''); | sb.append("appid='").append(appid).append('\''); | ||||
| sb.append(", sub_appid='").append(sub_appid).append('\''); | sb.append(", sub_appid='").append(sub_appid).append('\''); | ||||
| sb.append(", mch_id='").append(mch_id).append('\''); | sb.append(", mch_id='").append(mch_id).append('\''); | ||||
| @@ -0,0 +1,50 @@ | |||||
| package com.iformall.pay; | |||||
| import lombok.Data; | |||||
| /** | |||||
| * Created by Stormeye on 2018/11/7. | |||||
| * 服务商模式 | |||||
| */ | |||||
| @Data | |||||
| public class WxNativePayOrderP { | |||||
| private String appid; // 小程序ID | |||||
| private String mch_id; // 商户号 | |||||
| private String device_info; // 终端设备号 - 门店编号 | |||||
| private String nonce_str; // 随机字符串 | |||||
| private String sign; // 签名 | |||||
| private String sign_type; // 签名类型 | |||||
| private String body; // 商品简单描述 128 | |||||
| private String out_trade_no; // 商户订单号 | |||||
| private Integer total_fee; // 支付金额 | |||||
| private String spbill_create_ip; // 支付IP | |||||
| private String time_start; // 开始时间 | |||||
| private String time_expire; // 失效时间 | |||||
| private String notify_url; // 通知地址 | |||||
| private String trade_type; // 支付类型 | |||||
| private String product_id; // 商品ID | |||||
| private String profit_sharing; // 是否开启分账 | |||||
| @Override | |||||
| public String toString() { | |||||
| final StringBuilder sb = new StringBuilder("WxNativePayOrderP {"); | |||||
| sb.append("appid='").append(appid).append('\''); | |||||
| sb.append(", mch_id='").append(mch_id).append('\''); | |||||
| sb.append(", device_info='").append(device_info).append('\''); | |||||
| sb.append(", nonce_str='").append(nonce_str).append('\''); | |||||
| sb.append(", sign='").append(sign).append('\''); | |||||
| sb.append(", sign_type='").append(sign_type).append('\''); | |||||
| sb.append(", body='").append(body).append('\''); | |||||
| sb.append(", out_trade_no='").append(out_trade_no).append('\''); | |||||
| sb.append(", total_fee=").append(total_fee); | |||||
| sb.append(", spbill_create_ip='").append(spbill_create_ip).append('\''); | |||||
| sb.append(", time_start='").append(time_start).append('\''); | |||||
| sb.append(", time_expire='").append(time_expire).append('\''); | |||||
| sb.append(", notify_url='").append(notify_url).append('\''); | |||||
| sb.append(", trade_type='").append(trade_type).append('\''); | |||||
| sb.append(", product_id='").append(product_id).append('\''); | |||||
| sb.append(", profit_sharing='").append(profit_sharing).append('\''); | |||||
| sb.append('}'); | |||||
| return sb.toString(); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,54 @@ | |||||
| package com.iformall.pay; | |||||
| import lombok.Data; | |||||
| /** | |||||
| * Created by Stormeye on 2018/11/7. | |||||
| * 服务商模式 | |||||
| */ | |||||
| @Data | |||||
| public class WxNativePayOrderSP { | |||||
| private String appid; // 公众账号ID | |||||
| private String sub_appid; // 小程序ID | |||||
| private String mch_id; // 服务商号 | |||||
| private String sub_mch_id; // 特约商户号 | |||||
| private String device_info; // 终端设备号 - 门店编号 | |||||
| private String nonce_str; // 随机字符串 | |||||
| private String sign; // 签名 | |||||
| private String sign_type; // 签名类型 | |||||
| private String body; // 商品简单描述 128 | |||||
| private String out_trade_no; // 商户订单号 | |||||
| private Integer total_fee; // 支付金额 | |||||
| private String spbill_create_ip; // 支付IP | |||||
| private String time_start; // 开始时间 | |||||
| private String time_expire; // 失效时间 | |||||
| private String notify_url; // 通知地址 | |||||
| private String trade_type; // 支付类型 | |||||
| private String product_id; // 商品ID | |||||
| private String profit_sharing; // 是否开启分账 | |||||
| @Override | |||||
| public String toString() { | |||||
| final StringBuilder sb = new StringBuilder("WxNativePayOrderSP {"); | |||||
| sb.append("appid='").append(appid).append('\''); | |||||
| sb.append(", sub_appid='").append(sub_appid).append('\''); | |||||
| sb.append(", mch_id='").append(mch_id).append('\''); | |||||
| sb.append(", sub_mch_id='").append(sub_mch_id).append('\''); | |||||
| sb.append(", device_info='").append(device_info).append('\''); | |||||
| sb.append(", nonce_str='").append(nonce_str).append('\''); | |||||
| sb.append(", sign='").append(sign).append('\''); | |||||
| sb.append(", sign_type='").append(sign_type).append('\''); | |||||
| sb.append(", body='").append(body).append('\''); | |||||
| sb.append(", out_trade_no='").append(out_trade_no).append('\''); | |||||
| sb.append(", total_fee=").append(total_fee); | |||||
| sb.append(", spbill_create_ip='").append(spbill_create_ip).append('\''); | |||||
| sb.append(", time_start='").append(time_start).append('\''); | |||||
| sb.append(", time_expire='").append(time_expire).append('\''); | |||||
| sb.append(", notify_url='").append(notify_url).append('\''); | |||||
| sb.append(", trade_type='").append(trade_type).append('\''); | |||||
| sb.append(", product_id='").append(product_id).append('\''); | |||||
| sb.append(", profit_sharing='").append(profit_sharing).append('\''); | |||||
| sb.append('}'); | |||||
| return sb.toString(); | |||||
| } | |||||
| } | |||||
| @@ -33,6 +33,12 @@ public interface WxAppinfoService { | |||||
| * @return | * @return | ||||
| */ | */ | ||||
| WxAppinfo getByAppId(String appId); | WxAppinfo getByAppId(String appId); | ||||
| /** | |||||
| * 获取c端小程序信息 | |||||
| * @return | |||||
| */ | |||||
| WxAppinfo getCAppInfo(String tenantId); | |||||
| /** | /** | ||||
| * 保存或更新实体 | * 保存或更新实体 | ||||
| @@ -0,0 +1,61 @@ | |||||
| package com.iformall.service; | |||||
| import com.github.pagehelper.PageInfo; | |||||
| import com.iformall.common.ResultData; | |||||
| import com.iformall.domain.po.MallUserInfo; | |||||
| import com.iformall.domain.po.WxSubsidy; | |||||
| import com.iformall.enums.EnumPayWay; | |||||
| import java.util.Map; | |||||
| public interface WxSubsidyService { | |||||
| /** | |||||
| * 创建补贴订单 | |||||
| * @param amount | |||||
| * @return | |||||
| */ | |||||
| ResultData createSubsidy(MallUserInfo user, String ip, Integer amount); | |||||
| /** | |||||
| * 根据实体查询分页列表 | |||||
| * | |||||
| * @param record | |||||
| * @param pageIndex | |||||
| * @param pageSize | |||||
| * @return | |||||
| */ | |||||
| PageInfo<WxSubsidy> listAsPage(WxSubsidy record, Integer pageIndex, Integer pageSize); | |||||
| /** | |||||
| * 根据Id获得实体 | |||||
| * | |||||
| * @param id | |||||
| * @return | |||||
| */ | |||||
| WxSubsidy getById(Long id); | |||||
| /** | |||||
| * 保存或更新实体 | |||||
| * | |||||
| * @param record | |||||
| */ | |||||
| void saveOrUpdate(WxSubsidy record); | |||||
| /** | |||||
| * 根据Id删除实体 | |||||
| * | |||||
| * @param id | |||||
| */ | |||||
| void deleteById(Long id); | |||||
| String notify(Map<String, String> paramMap, EnumPayWay payWay); | |||||
| } | |||||
| @@ -38,6 +38,19 @@ public class WxAppinfoServiceImpl implements WxAppinfoService { | |||||
| @Autowired | @Autowired | ||||
| WxAppinfoMapper wxAppinfoMapper; | WxAppinfoMapper wxAppinfoMapper; | ||||
| @Override | |||||
| public WxAppinfo getCAppInfo(String tenantId) { | |||||
| WxAppinfo appInfo = null; | |||||
| WxAppinfo appinfoQ = new WxAppinfo(); | |||||
| appinfoQ.setTenantId(tenantId); | |||||
| appinfoQ.setType(EnumAppType.C.getCode()); | |||||
| List<WxAppinfo> appList = wxAppinfoMapper.select(appinfoQ); | |||||
| if (appList.size() > 0) { | |||||
| appInfo = appList.get(0); | |||||
| } | |||||
| return appInfo; | |||||
| } | |||||
| @Override | @Override | ||||
| public WxAppinfo getByAppId(String appId) { | public WxAppinfo getByAppId(String appId) { | ||||
| return wxAppinfoMapper.findByAppId(appId); | return wxAppinfoMapper.findByAppId(appId); | ||||
| @@ -71,6 +71,9 @@ public class WxOrderServiceImpl implements WxOrderService { | |||||
| @Autowired | @Autowired | ||||
| WxMerchantBUserMapper wxMerchantBUserMapper; | WxMerchantBUserMapper wxMerchantBUserMapper; | ||||
| @Autowired | |||||
| WxAppinfoService wxAppinfoService; | |||||
| @Autowired | @Autowired | ||||
| WxPayOrderService wxPayOrderService; | WxPayOrderService wxPayOrderService; | ||||
| @@ -870,14 +873,7 @@ public class WxOrderServiceImpl implements WxOrderService { | |||||
| @Override | @Override | ||||
| public ResultData orderClose(WxOrder order) { | public ResultData orderClose(WxOrder order) { | ||||
| // 1. get c appid | // 1. get c appid | ||||
| WxAppinfo appInfo = null; | |||||
| WxAppinfo appinfoQ = new WxAppinfo(); | |||||
| appinfoQ.setTenantId(order.getTenantId()); | |||||
| appinfoQ.setType(EnumAppType.C.getCode()); | |||||
| List<WxAppinfo> appList = wxAppinfoMapper.select(appinfoQ); | |||||
| if (appList.size() > 0) { | |||||
| appInfo = appList.get(0); | |||||
| } | |||||
| WxAppinfo appInfo = wxAppinfoService.getCAppInfo(order.getTenantId()); | |||||
| // 2. get pay order | // 2. get pay order | ||||
| WxPayOrder payOrderQ = new WxPayOrder(); | WxPayOrder payOrderQ = new WxPayOrder(); | ||||
| payOrderQ.setTenantId(order.getTenantId()); | payOrderQ.setTenantId(order.getTenantId()); | ||||
| @@ -13,6 +13,7 @@ import com.iformall.enums.*; | |||||
| import com.iformall.exception.MallinkException; | import com.iformall.exception.MallinkException; | ||||
| import com.iformall.mapper.*; | import com.iformall.mapper.*; | ||||
| import com.iformall.pay.*; | import com.iformall.pay.*; | ||||
| import com.iformall.service.WxAppinfoService; | |||||
| import com.iformall.service.WxCouponOrderService; | import com.iformall.service.WxCouponOrderService; | ||||
| import com.iformall.service.WxOrderService; | import com.iformall.service.WxOrderService; | ||||
| import com.iformall.service.WxPayOrderService; | import com.iformall.service.WxPayOrderService; | ||||
| @@ -70,6 +71,9 @@ public class WxPayOrderServiceImpl implements WxPayOrderService { | |||||
| @Autowired | @Autowired | ||||
| WxCouponOrderService wxCouponOrderService; | WxCouponOrderService wxCouponOrderService; | ||||
| @Autowired | |||||
| WxAppinfoService wxAppinfoService; | |||||
| JSONObject errorMap = JSON.parseObject("{" + | JSONObject errorMap = JSON.parseObject("{" + | ||||
| "\"NOAUTH\":{\"detail\":\"商户无此接口权限\",\"reason\":\"商户未开通此接口权限\",\"resolution\":\"请商户前往申请此接口权限\"}," + | "\"NOAUTH\":{\"detail\":\"商户无此接口权限\",\"reason\":\"商户未开通此接口权限\",\"resolution\":\"请商户前往申请此接口权限\"}," + | ||||
| "\"NOTENOUGH\":{\"detail\":\"余额不足\",\"reason\":\"用户帐号余额不足\",\"resolution\":\"用户帐号余额不足,请用户充值或更换支付卡后再支付\"}," + | "\"NOTENOUGH\":{\"detail\":\"余额不足\",\"reason\":\"用户帐号余额不足\",\"resolution\":\"用户帐号余额不足,请用户充值或更换支付卡后再支付\"}," + | ||||
| @@ -367,7 +371,7 @@ public class WxPayOrderServiceImpl implements WxPayOrderService { | |||||
| public ResultData createMicroPayOrder(boolean isReal, WxMerchantBUser user, WxPayOrder record, EnumPayWay payWay) { | public ResultData createMicroPayOrder(boolean isReal, WxMerchantBUser user, WxPayOrder record, EnumPayWay payWay) { | ||||
| final IdWorker idworker = IdWorker.get(); | final IdWorker idworker = IdWorker.get(); | ||||
| WxAppinfo appInfo = getWxAppinfo(EnumOrderType.MICROPAY.getCode(), user.getTenantId()); | |||||
| WxAppinfo appInfo = wxAppinfoService.getCAppInfo(user.getTenantId()); | |||||
| EnumPayShare isShare = EnumPayShare.NO; | EnumPayShare isShare = EnumPayShare.NO; | ||||
| @@ -686,7 +690,7 @@ public class WxPayOrderServiceImpl implements WxPayOrderService { | |||||
| return new ResultData(ErrorCode.PAY_ORDER_ERROR.getCode(), "Order not found"); | return new ResultData(ErrorCode.PAY_ORDER_ERROR.getCode(), "Order not found"); | ||||
| } | } | ||||
| WxAppinfo appInfo = getWxAppinfo(order.getType(), record.getTenantId()); | |||||
| WxAppinfo appInfo = wxAppinfoService.getCAppInfo(record.getTenantId()); | |||||
| String response = wechatPayOrderQuery(appInfo, record); | String response = wechatPayOrderQuery(appInfo, record); | ||||
| @@ -948,7 +952,7 @@ public class WxPayOrderServiceImpl implements WxPayOrderService { | |||||
| @Override | @Override | ||||
| public ResultData payOrderReverse(WxPayOrder record) { | public ResultData payOrderReverse(WxPayOrder record) { | ||||
| WxAppinfo appInfo = getWxAppinfo(EnumOrderType.MICROPAY.getCode(), record.getTenantId()); | |||||
| WxAppinfo appInfo = wxAppinfoService.getCAppInfo(record.getTenantId()); | |||||
| try { | try { | ||||
| String response = wechatPayOrderReverse(appInfo, record); | String response = wechatPayOrderReverse(appInfo, record); | ||||
| @@ -1239,7 +1243,7 @@ public class WxPayOrderServiceImpl implements WxPayOrderService { | |||||
| throw new MallinkException(ErrorCode.ORDER_IS_NOT_FIND); | throw new MallinkException(ErrorCode.ORDER_IS_NOT_FIND); | ||||
| } | } | ||||
| WxAppinfo appinfo = getWxAppinfo(EnumOrderType.MICROPAY.getCode(), order.getTenantId()); | |||||
| WxAppinfo appinfo = wxAppinfoService.getCAppInfo(order.getTenantId()); | |||||
| // add c_user | // add c_user | ||||
| Date curDate = new Date(); | Date curDate = new Date(); | ||||
| @@ -1365,7 +1369,7 @@ public class WxPayOrderServiceImpl implements WxPayOrderService { | |||||
| if (record.getId() > 0) { | if (record.getId() > 0) { | ||||
| // 有价券 | // 有价券 | ||||
| // 1. get appinfo | // 1. get appinfo | ||||
| WxAppinfo appInfo = getWxAppinfo(order.getType(), order.getTenantId()); | |||||
| WxAppinfo appInfo = wxAppinfoService.getCAppInfo(order.getTenantId()); | |||||
| if (2 == status) { | if (2 == status) { | ||||
| // 前端支付取消, | // 前端支付取消, | ||||
| @@ -0,0 +1,368 @@ | |||||
| package com.iformall.service.impl; | |||||
| import com.alibaba.fastjson.JSON; | |||||
| import com.alibaba.fastjson.JSONObject; | |||||
| import com.github.pagehelper.PageHelper; | |||||
| import com.github.pagehelper.PageInfo; | |||||
| import com.iformall.common.ErrorCode; | |||||
| import com.iformall.common.Result; | |||||
| import com.iformall.common.ResultData; | |||||
| import com.iformall.domain.po.*; | |||||
| import com.iformall.enums.EnumPayShare; | |||||
| import com.iformall.enums.EnumPayStatus; | |||||
| import com.iformall.enums.EnumPayWay; | |||||
| import com.iformall.exception.MallinkException; | |||||
| import com.iformall.mapper.WxAppinfoMapper; | |||||
| import com.iformall.mapper.WxPayAccountMapper; | |||||
| import com.iformall.mapper.WxSubsidyMapper; | |||||
| import com.iformall.pay.WxNativePayOrderSP; | |||||
| import com.iformall.pay.WxPay; | |||||
| import com.iformall.pay.WxPayment; | |||||
| import com.iformall.service.WxAppinfoService; | |||||
| import com.iformall.service.WxSubsidyService; | |||||
| import com.iformall.utils.BeanUtils; | |||||
| import com.iformall.utils.Utility; | |||||
| import com.iformall.utils.XmlUtil; | |||||
| import org.apache.commons.lang3.StringUtils; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.stereotype.Service; | |||||
| import com.iformall.common.IdWorker; | |||||
| import java.text.ParseException; | |||||
| import java.util.*; | |||||
| @Service | |||||
| public class WxSubsidyServiceImpl implements WxSubsidyService { | |||||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | |||||
| @Autowired | |||||
| WxSubsidyMapper wxSubsidyMapper; | |||||
| @Autowired | |||||
| WxAppinfoMapper wxAppinfoMapper; | |||||
| @Autowired | |||||
| WxAppinfoService wxAppinfoService; | |||||
| @Autowired | |||||
| WxPayAccountMapper wxPayAccountMapper; | |||||
| JSONObject errorMap = JSON.parseObject("{" + | |||||
| "\"INVALID_REQUEST\":{\"detail\":\"参数错误\",\"reason\":\"参数格式有误或者未按规则上传\",\"resolution\":\"订单重入时,要求参数值与原请求一致,请确认参数问题\"}," + | |||||
| "\"NOAUTH\":{\"detail\":\"商户无此接口权限\",\"reason\":\"商户未开通此接口权限\",\"resolution\":\"请商户前往申请此接口权限\"}," + | |||||
| "\"NOTENOUGH\":{\"detail\":\"余额不足\",\"reason\":\"用户帐号余额不足\",\"resolution\":\"用户帐号余额不足,请用户充值或更换支付卡后再支付\"}," + | |||||
| "\"ORDERPAID\":{\"detail\":\"商户订单已支付\",\"reason\":\"商户订单已支付,无需重复操作\",\"resolution\":\"商户订单已支付,无需更多操作\"}," + | |||||
| "\"ORDERCLOSED\":{\"detail\":\"订单已关闭\",\"reason\":\"当前订单已关闭,无法支付\",\"resolution\":\"当前订单已关闭,请重新下单\"}," + | |||||
| "\"SYSTEMERROR\":{\"detail\":\"系统错误\t\",\"reason\":\"系统超时\",\"resolution\":\"系统异常,请用相同参数重新调用\"}," + | |||||
| "\"APPID_NOT_EXIST\":{\"detail\":\"APPID不存在\",\"reason\":\"参数中缺少APPID\",\"resolution\":\"请检查APPID是否正确\"}," + | |||||
| "\"MCHID_NOT_EXIST\":{\"detail\":\"MCHID不存在\",\"reason\":\"参数中缺少MCHID\",\"resolution\":\"请检查MCHID是否正确\"}," + | |||||
| "\"APPID_MCHID_NOT_MATCH\":{\"detail\":\"appid和mch_id不匹配\",\"reason\":\"appid和mch_id不匹配\",\"resolution\":\"请确认appid和mch_id是否匹配\"}," + | |||||
| "\"LACK_PARAMS\":{\"detail\":\"缺少参数\t\",\"reason\":\"缺少必要的请求参数\",\"resolution\":\"请检查参数是否齐全\"}," + | |||||
| "\"OUT_TRADE_NO_USED\":{\"detail\":\"商户订单号重复\",\"reason\":\"同一笔交易不能多次提交\",\"resolution\":\"请核实商户订单号是否重复提交\"}," + | |||||
| "\"SIGNERROR\":{\"detail\":\"签名错误\",\"reason\":\"参数签名结果不正确\",\"resolution\":\"请检查签名参数和方法是否都符合签名算法要求\"}," + | |||||
| "\"XML_FORMAT_ERROR\":{\"detail\":\"XML格式错误\t\",\"reason\":\"XML格式错误\",\"resolution\":\"请检查XML参数格式是否正确\"}," + | |||||
| "\"REQUIRE_POST_METHOD\":{\"detail\":\"请使用post方法\",\"reason\":\"未使用post传递参数\",\"resolution\":\"请检查请求参数是否通过post方法提交\"}," + | |||||
| "\"POST_DATA_EMPTY\":{\"detail\":\"post数据为空\",\"reason\":\"post数据不能为空\",\"resolution\":\"请检查post数据是否为空\"}," + | |||||
| "\"NOT_UTF8\":{\"detail\":\"编码格式错误\",\"reason\":\"未使用指定编码格式\",\"resolution\":\"请使用UTF-8编码格式\"}}"); | |||||
| JSONObject errorMapQuery = JSON.parseObject("{" + | |||||
| "\"ORDERNOTEXIST\":{\"detail\":\"此交易订单号不存在\",\"reason\":\"查询系统中不存在此交易订单号\",\"resolution\":\"该API只能查提交支付交易返回成功的订单,请商户检查需要查询的订单号是否正确\"},\n" + | |||||
| "\"SYSTEMERROR\":{\"detail\":\"系统错误\t\",\"reason\":\"后台系统返回错误\",\"resolution\":\"系统异常,请再调用发起查询\"}}"); | |||||
| JSONObject errorMapClose = JSON.parseObject("{" + | |||||
| "\"ORDERPAID\":{\"detail\":\"订单已支付\",\"reason\":\"订单已支付,不能发起关单\",\"resolution\":\"订单已支付,不能发起关单,请当作已支付的正常交易\"}," + | |||||
| "\"SYSTEMERROR\":{\"detail\":\"系统错误\",\"reason\":\"系统错误\",\"resolution\":\"系统异常,请重新调用该API\"}," + | |||||
| "\"ORDERCLOSED\":{\"detail\":\"订单已关闭\",\"reason\":\"订单已关闭,无法重复关闭\",\"resolution\":\"订单已关闭,无需继续调用\"}," + | |||||
| "\"SIGNERROR\":{\"detail\":\"签名错误\",\"reason\":\"参数签名结果不正确\",\"resolution\":\"请检查签名参数和方法是否都符合签名算法要求\"}," + | |||||
| "\"REQUIRE_POST_METHOD\":{\"detail\":\"请使用post方法\",\"reason\":\"未使用post传递参数\",\"resolution\":\"请检查请求参数是否通过post方法提交\"}," + | |||||
| "\"XML_FORMAT_ERROR\":{\"detail\":\"XML格式错误\t\",\"reason\":\"XML格式错误\",\"reason\":\"请检查XML参数格式是否正确\"}}"); | |||||
| @Override | |||||
| public ResultData createSubsidy(MallUserInfo user, String ip, Integer amount) { | |||||
| final IdWorker idWorker = IdWorker.get(); | |||||
| EnumPayShare isShare = EnumPayShare.NO; | |||||
| Date curDate = new Date(); | |||||
| // 1. 获取c端小程序 | |||||
| WxAppinfo appInfo = wxAppinfoService.getCAppInfo(user.getTenantId()); | |||||
| // 2. 获取payAccount | |||||
| WxPayAccount payAccount = wxPayAccountMapper.selectByPrimaryKey(appInfo.getPayId()); | |||||
| if (payAccount.getShare().intValue() > 0) { | |||||
| isShare = EnumPayShare.YES; | |||||
| } | |||||
| // 3. 补贴订单 | |||||
| Integer totalFee = amount * 100; // 元->分 | |||||
| Long id = idWorker.nextId(); | |||||
| String payOrderNo = String.valueOf(id); | |||||
| WxSubsidy record = new WxSubsidy(); | |||||
| record.setId(id); | |||||
| record.setTenantId(user.getTenantId()); | |||||
| record.setOperatorUserId(user.getId()); | |||||
| record.setCreateTime(curDate); | |||||
| record.setUpdateTime(curDate); | |||||
| record.setOrderNo(payOrderNo); | |||||
| record.setBody("商场补贴-"+amount + "元"); | |||||
| record.setIp(ip); | |||||
| record.setStatus(EnumPayStatus.PAY_STATUS_WAIT.getCode()); | |||||
| record.setPayTimeStart(curDate); | |||||
| record.setPayTimeEnd(curDate); | |||||
| record.setAmount(totalFee); | |||||
| // 分账金额 | |||||
| Double dChargeFee = Math.ceil(record.getAmount() * 1.0D * payAccount.getRate() / 10000); | |||||
| Integer share_amount = record.getAmount() - dChargeFee.intValue(); | |||||
| record.setShareAmount(share_amount); | |||||
| try { | |||||
| wxSubsidyMapper.insertSelective(record); | |||||
| } catch (Exception e) { | |||||
| logger.error("wx_subsidy save fail" + e.getMessage()); | |||||
| return new ResultData(ErrorCode.DB_FAIL); | |||||
| } | |||||
| // 3. 支付发起 | |||||
| try { | |||||
| // 统一下单 // 服务商模式 | |||||
| String noncestr = Utility.generate32UUID(); | |||||
| WxNativePayOrderSP payOrder = new WxNativePayOrderSP(); | |||||
| payOrder.setAppid(appInfo.getParentAppId()); | |||||
| payOrder.setSub_appid(appInfo.getAppId()); | |||||
| payOrder.setMch_id(payAccount.getMchId()); | |||||
| payOrder.setSub_mch_id(payAccount.getSubMchId()); | |||||
| payOrder.setDevice_info("WEB"); | |||||
| payOrder.setNonce_str(noncestr); | |||||
| payOrder.setBody(record.getBody()); | |||||
| payOrder.setOut_trade_no(record.getOrderNo()); | |||||
| payOrder.setTotal_fee(record.getAmount()); | |||||
| payOrder.setSpbill_create_ip(record.getIp()); | |||||
| payOrder.setTime_start(Utility.getDataFormatStringYYYYMMDDHHmmss(curDate)); | |||||
| Date futureDate = new Date(); | |||||
| futureDate.setTime(curDate.getTime() + 15 * 60 * 1000); | |||||
| payOrder.setTime_expire(Utility.getDataFormatStringYYYYMMDDHHmmss(futureDate)); // 15分钟后结束 | |||||
| payOrder.setNotify_url(payAccount.getNotifyUrl() + "/subsidyPay"); | |||||
| payOrder.setTrade_type("NATIVE"); | |||||
| payOrder.setProduct_id(record.getOrderNo()); | |||||
| payOrder.setSign_type("HMAC-SHA256"); | |||||
| payOrder.setProfit_sharing(null); | |||||
| if (isShare == EnumPayShare.YES) { | |||||
| payOrder.setProfit_sharing("Y"); | |||||
| } | |||||
| Map<String, String> payOrderMap = BeanUtils.toStringMap(payOrder); | |||||
| payOrder.setSign(WxPayment.createSignHMAC(payOrderMap, payAccount.getApiKey())); | |||||
| String response = WxPay.pushOrder(BeanUtils.toStringMap(payOrder)); | |||||
| logger.info("wx_subsidy wechat native Pay, " + payOrder.toString() + ", response: " + response.toString()); | |||||
| Map<String, String> returnMap = WxPayment.xmlToMap(response); | |||||
| String return_code = returnMap.get("return_code"); | |||||
| String result_code = returnMap.get("result_code"); | |||||
| if ("SUCCESS".equalsIgnoreCase(return_code)) { | |||||
| if ("SUCCESS".equals(result_code)) { | |||||
| String prepay_id = returnMap.get("prepay_id"); | |||||
| String code_url = returnMap.get("code_url"); | |||||
| // update payOrder with prepay_id | |||||
| record.setPrepayId(prepay_id); | |||||
| record.setCodeUrl(code_url); | |||||
| record.setUpdateTime(new Date()); | |||||
| try { | |||||
| wxSubsidyMapper.updateByPrimaryKeySelective(record); | |||||
| } catch (Exception e) { | |||||
| logger.error("wx_subsidy update error: " + record.toString()); | |||||
| throw new MallinkException(ErrorCode.DB_FAIL); | |||||
| } | |||||
| Map<String, String> retMap = new HashMap<String, String>(); | |||||
| retMap.put("code_url", code_url); | |||||
| return new ResultData(Result.SUCCESS, "创建支付订单成功", retMap); | |||||
| } else { | |||||
| String errMsg = ""; | |||||
| JSONObject errObj = errorMap.getJSONObject(result_code); | |||||
| if (errObj != null) { | |||||
| errMsg = errObj.toJSONString(); | |||||
| record.setFailReason(errMsg); | |||||
| } else { | |||||
| errMsg = returnMap.get("return_msg"); | |||||
| record.setFailReason(errMsg); | |||||
| } | |||||
| record.setUpdateTime(new Date()); | |||||
| try { | |||||
| wxSubsidyMapper.updateByPrimaryKeySelective(record); | |||||
| } catch (Exception e) { | |||||
| logger.error("pay order update error: " + record.toString()); | |||||
| throw new MallinkException(ErrorCode.DB_FAIL); | |||||
| } | |||||
| return new ResultData(ErrorCode.PAY_ORDER_ERROR.getCode(), errMsg, returnMap); | |||||
| } | |||||
| } else { | |||||
| String errMsg = returnMap.get("return_msg"); | |||||
| record.setFailReason(errMsg); | |||||
| record.setUpdateTime(new Date()); | |||||
| try { | |||||
| wxSubsidyMapper.updateByPrimaryKeySelective(record); | |||||
| } catch (Exception e) { | |||||
| logger.error("pay order update error: " + record.toString()); | |||||
| throw new MallinkException(ErrorCode.DB_FAIL); | |||||
| } | |||||
| return new ResultData(ErrorCode.PAY_ORDER_ERROR.getCode(), errMsg, returnMap); | |||||
| } | |||||
| } catch (RuntimeException e) { | |||||
| throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage()); | |||||
| } catch (Exception e) { | |||||
| throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage()); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * 提供微信支付回调调用 | |||||
| * | |||||
| * @param paramMap 异步通知参数 | |||||
| * @param payWay 支付方式 | |||||
| * @return | |||||
| */ | |||||
| @Override | |||||
| public String notify(Map<String, String> paramMap, EnumPayWay payWay) { | |||||
| // how to get wechatAppId, wechatMchId, partnerKey | |||||
| String appId = paramMap.get("appid"); | |||||
| String subAppId = paramMap.get("sub_appid"); | |||||
| String mchId = paramMap.get("mch_id"); | |||||
| String subMchId = paramMap.get("sub_mch_id"); | |||||
| WxAppinfo appinfo = null; | |||||
| boolean isNormal = true; | |||||
| if (StringUtils.isBlank(subAppId) && StringUtils.isBlank(subMchId)) { | |||||
| // 普通商户号 | |||||
| appinfo = wxAppinfoMapper.findByAppId(appId); | |||||
| if (appinfo == null) { | |||||
| logger.error("appid not found: " + appId); | |||||
| throw new MallinkException(ErrorCode.APP_ID_NOT_FOUND); | |||||
| } | |||||
| isNormal = true; | |||||
| } else { | |||||
| // 服务号 现在用hmac-sha256 | |||||
| appinfo = wxAppinfoMapper.findByAppId(subAppId); | |||||
| if (appinfo == null) { | |||||
| logger.error("subappid not found: " + subAppId); | |||||
| throw new MallinkException(ErrorCode.APP_ID_NOT_FOUND); | |||||
| } | |||||
| isNormal = false; | |||||
| } | |||||
| WxPayAccount payAccount = wxPayAccountMapper.selectByPrimaryKey(appinfo.getPayId()); | |||||
| if (payAccount == null) { | |||||
| throw new MallinkException(ErrorCode.MCH_INFO_NOT_FOUND); | |||||
| } | |||||
| String partnerKey = payAccount.getApiKey(); | |||||
| try { | |||||
| if (payWay == EnumPayWay.PAY_WAY_WEAPP) { | |||||
| boolean signVerified = false; | |||||
| if (isNormal) { | |||||
| // 普通商户号支付 | |||||
| signVerified = WxPayment.verifyNotify(paramMap, partnerKey); | |||||
| if (!signVerified) { | |||||
| logger.warn("notify order, wxpay checksign error, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); | |||||
| throw new MallinkException(ErrorCode.PAY_ORDER_NOTIFY_CHECK_SIGN_ERROR); | |||||
| } | |||||
| } else { | |||||
| // 服务号 现在用hmac-sha256 | |||||
| signVerified = WxPayment.verifyNotifyHMAC(paramMap, partnerKey); | |||||
| if (!signVerified) { | |||||
| logger.warn("notify order, wxpay checksign error, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); | |||||
| throw new MallinkException(ErrorCode.PAY_ORDER_NOTIFY_CHECK_SIGN_ERROR); | |||||
| } | |||||
| } | |||||
| if (!"SUCCESS".equals(paramMap.get("return_code"))) { | |||||
| logger.warn("notify order, wxpay status not success, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); | |||||
| SortedMap resultMap = new TreeMap(); | |||||
| resultMap.put("return_code", "FAIL"); | |||||
| resultMap.put("return_msg", "订单状态码非SUCCESS"); | |||||
| return XmlUtil.getRequestXml(resultMap); | |||||
| } | |||||
| String payOrderNo = paramMap.get("out_trade_no"); | |||||
| String transactionId = paramMap.get("transaction_id"); | |||||
| String timEndStr = paramMap.get("time_end"); | |||||
| Long payOrderId = Long.valueOf(payOrderNo); | |||||
| WxSubsidy subsidy = wxSubsidyMapper.selectByPrimaryKey(payOrderId); | |||||
| if (subsidy == null) { | |||||
| logger.warn("notify order, wxpay check pay order not exists, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); | |||||
| SortedMap resultMap = new TreeMap(); | |||||
| resultMap.put("return_code", "FAIL"); | |||||
| resultMap.put("return_msg", "订单不存在"); | |||||
| return XmlUtil.getRequestXml(resultMap); | |||||
| } | |||||
| // 验证支付金额 | |||||
| if (!paramMap.get("total_fee").equals(subsidy.getAmount().toString())) { | |||||
| logger.warn("notify order, wxpay check total_fee is invalid, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); | |||||
| SortedMap resultMap = new TreeMap(); | |||||
| resultMap.put("return_code", "FAIL"); | |||||
| resultMap.put("return_msg", "订单总金额不一致"); | |||||
| return XmlUtil.getRequestXml(resultMap); | |||||
| } | |||||
| Date timeEnd = null; | |||||
| try { | |||||
| timeEnd = Utility.getDateFromString(timEndStr); | |||||
| } catch (ParseException e) { | |||||
| logger.error("解析timeEnd失败"); | |||||
| timeEnd = new Date(); | |||||
| } | |||||
| subsidy.setPayTimeEnd(timeEnd); | |||||
| subsidy.setUpdateTime(new Date()); | |||||
| try { | |||||
| wxSubsidyMapper.updateByPrimaryKeySelective(subsidy); | |||||
| } catch (Exception e) { | |||||
| logger.error("wx_subsidy update exception"); | |||||
| } | |||||
| logger.info("notify order, wxpay checksign success, paramMap:{}, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString()); | |||||
| SortedMap resultMap = new TreeMap(); | |||||
| resultMap.put("return_code", "SUCCESS"); | |||||
| resultMap.put("return_msg", "OK"); | |||||
| return XmlUtil.getRequestXml(resultMap); | |||||
| } | |||||
| } catch (RuntimeException e) { | |||||
| logger.warn("notify order, checksign error, paramMap: " + paramMap.toString() + ", payWay:" + payWay.toString() + ", e:" + e.getMessage()); | |||||
| throw new MallinkException(ErrorCode.PAY_ORDER_ERROR); | |||||
| } | |||||
| SortedMap resultMap = new TreeMap(); | |||||
| resultMap.put("return_code", "FAIL"); | |||||
| resultMap.put("return_msg", "FAILED"); | |||||
| return XmlUtil.getRequestXml(resultMap); | |||||
| } | |||||
| @Override | |||||
| public PageInfo<WxSubsidy> listAsPage(WxSubsidy record, Integer pageIndex, Integer pageSize) { | |||||
| return PageHelper.startPage(pageIndex, pageSize).doSelectPageInfo(() -> wxSubsidyMapper.findList(record)); | |||||
| } | |||||
| @Override | |||||
| public WxSubsidy getById(Long id) { | |||||
| return wxSubsidyMapper.selectByPrimaryKey(id); | |||||
| } | |||||
| @Override | |||||
| public void saveOrUpdate(WxSubsidy record) { | |||||
| if (record.getId() == null) { | |||||
| //record.setId(UUID.randomUUID().toString().replaceAll("-", "")); | |||||
| final IdWorker idWorker = IdWorker.get(); | |||||
| record.setId(idWorker.nextId()); | |||||
| wxSubsidyMapper.insertSelective(record); | |||||
| } else { | |||||
| wxSubsidyMapper.updateByPrimaryKeySelective(record); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void deleteById(Long id) { | |||||
| wxSubsidyMapper.deleteByPrimaryKey(id); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,103 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | |||||
| <mapper namespace="com.iformall.mapper.WxSubsidyMapper"> | |||||
| <resultMap id="BaseResultMap" type="com.iformall.domain.po.WxSubsidy"> | |||||
| <id column="id" jdbcType="BIGINT" property="id" /> | |||||
| <result column="tenant_id" jdbcType="VARCHAR" property="tenantId" /> | |||||
| <result column="operator_user_id" jdbcType="VARCHAR" property="operatorUserId" /> | |||||
| <result column="create_time" jdbcType="TIMESTAMP" property="createTime" /> | |||||
| <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" /> | |||||
| <result column="order_no" jdbcType="VARCHAR" property="orderNo" /> | |||||
| <result column="body" jdbcType="VARCHAR" property="body" /> | |||||
| <result column="ip" jdbcType="VARCHAR" property="ip" /> | |||||
| <result column="status" jdbcType="INTEGER" property="status" /> | |||||
| <result column="pay_time_start" jdbcType="TIMESTAMP" property="payTimeStart" /> | |||||
| <result column="pay_time_end" jdbcType="TIMESTAMP" property="payTimeEnd" /> | |||||
| <result column="prepay_id" jdbcType="VARCHAR" property="prepayId" /> | |||||
| <result column="code_url" jdbcType="VARCHAR" property="codeUrl" /> | |||||
| <result column="transaction_id" jdbcType="VARCHAR" property="transactionId" /> | |||||
| <result column="amount" jdbcType="INTEGER" property="amount" /> | |||||
| <result column="share_amount" jdbcType="INTEGER" property="shareAmount" /> | |||||
| <result column="share_remain_amount" jdbcType="INTEGER" property="shareRemainAmount" /> | |||||
| <result column="fail_reason" jdbcType="VARCHAR" property="failReason" /> | |||||
| <result column="open_id" jdbcType="VARCHAR" property="openId" /> | |||||
| </resultMap> | |||||
| <sql id="allColumns"> | |||||
| `id`,`tenant_id`,`operator_user_id`,`create_time`,`update_time`,`order_no`,`body`,`total_fee`,`ip`,`status`, | |||||
| `pay_time_start`,`pay_time_end`,`prepay_id`,`code_url`,`transaction_id`,`amount`,`share_amount`,`share_remain_amount`,`fail_reason`,`open_id` | |||||
| </sql> | |||||
| <sql id="dynamicWhereConditions"> | |||||
| where 1 = 1 | |||||
| <if test=" null != id "> | |||||
| and `id` = #{id} | |||||
| </if> | |||||
| <if test=" null != tenantId "> | |||||
| and `tenant_id` = #{tenantId} | |||||
| </if> | |||||
| <if test=" null != createTime "> | |||||
| and `create_time` = #{createTime} | |||||
| </if> | |||||
| <if test=" null != updateTime "> | |||||
| and `update_time` = #{updateTime} | |||||
| </if> | |||||
| <if test=" null != orderNo "> | |||||
| and `order_no` like concat('%', #{orderNo},'%') | |||||
| </if> | |||||
| <if test=" null != body "> | |||||
| and `body` like concat('%', #{body},'%') | |||||
| </if> | |||||
| <if test=" null != ip "> | |||||
| and `ip` like concat('%', #{ip},'%') | |||||
| </if> | |||||
| <if test=" null != status "> | |||||
| and `status` = #{status} | |||||
| </if> | |||||
| <if test=" null != payTimeStart "> | |||||
| and `pay_time_start` = #{payTimeStart} | |||||
| </if> | |||||
| <if test=" null != payTimeEnd "> | |||||
| and `pay_time_end` = #{payTimeEnd} | |||||
| </if> | |||||
| <if test=" null != prepayId "> | |||||
| and `prepay_id` like concat('%', #{prepayId},'%') | |||||
| </if> | |||||
| <if test=" null != codeUrl "> | |||||
| and `code_url` like concat('%', #{codeUrl},'%') | |||||
| </if> | |||||
| <if test=" null != transactionId "> | |||||
| and `transaction_id` like concat('%', #{transactionId},'%') | |||||
| </if> | |||||
| <if test=" null != amount "> | |||||
| and `amount` = #{amount} | |||||
| </if> | |||||
| <if test=" null != shareAmount "> | |||||
| and `share_amount` = #{shareAmount} | |||||
| </if> | |||||
| <if test=" null != shareRemainAmount "> | |||||
| and `share_remain_amount` = #{shareRemainAmount} | |||||
| </if> | |||||
| <if test=" null != failReason "> | |||||
| and `fail_reason` like concat('%', #{failReason},'%') | |||||
| </if> | |||||
| <if test=" null != ids "> | |||||
| and id in | |||||
| <foreach collection="ids" index="index" item="idItem" open="(" separator="," close=")"> | |||||
| #{idItem} | |||||
| </foreach> | |||||
| </if> | |||||
| <if test=" null != sortColumns"> order by ${sortColumns} </if> | |||||
| </sql> | |||||
| <select id="findList" parameterType="com.iformall.domain.po.WxSubsidy" resultMap="BaseResultMap"> | |||||
| select <include refid="allColumns" /> from wx_subsidy | |||||
| <include refid="dynamicWhereConditions" /> | |||||
| </select> | |||||
| </mapper> | |||||