微信发红包接口实现

This commit is contained in:
egzosn
2020-05-17 23:36:25 +08:00
parent c31f918478
commit 7f90f489ce
5 changed files with 207 additions and 220 deletions

View File

@@ -2,27 +2,16 @@
package com.egzosn.pay.demo.controller;
import com.egzosn.pay.common.api.Callback;
import com.egzosn.pay.common.api.PayService;
import com.egzosn.pay.common.bean.*;
import com.egzosn.pay.common.http.HttpConfigStorage;
import com.egzosn.pay.common.http.UriVariables;
import com.egzosn.pay.demo.entity.PayType;
import com.egzosn.pay.demo.request.QueryOrder;
import com.egzosn.pay.demo.service.PayResponse;
import com.egzosn.pay.demo.service.handler.AliPayMessageHandler;
import com.egzosn.pay.demo.service.handler.WxPayMessageHandler;
import com.egzosn.pay.wx.api.WxPayConfigStorage;
import com.egzosn.pay.wx.api.WxPayService;
import com.egzosn.pay.wx.bean.WxBank;
import com.egzosn.pay.wx.bean.WxTransactionType;
import com.egzosn.pay.wx.bean.WxTransferType;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import com.egzosn.pay.wx.bean.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
@@ -43,7 +32,7 @@ import java.util.UUID;
@RequestMapping("wx")
public class WxPayController {
private PayService service = null;
private WxPayService service = null;
@@ -334,8 +323,8 @@ public class WxPayController {
* @return 返回支付方查询退款后的结果
*/
@RequestMapping("refundquery")
public Map<String, Object> refundquery(QueryOrder order) {
return service.refundquery(order.getTradeNo(), order.getOutTradeNo());
public Map<String, Object> refundquery(RefundOrder order) {
return service.refundquery(order);
}
/**
@@ -428,4 +417,51 @@ public class WxPayController {
//默认查询银行卡的记录 com.egzosn.pay.wx.bean.WxTransferType#QUERY_BANK
return service.transferQuery(outNo, wxTransferType);
}
/**
* 微信发红包
* @param redpackOrder 红包订单
* @return 结果
*/
public Map<String, Object> sendredpack(RedpackOrder redpackOrder) {
redpackOrder.setTransferType(WxSendredpackType.SENDREDPACK);
return service.sendredpack(redpackOrder);
}
/**
* 发放裂变红包
* @param redpackOrder 红包订单
* @return 结果
*/
public Map<String, Object> sendgroupredpack(RedpackOrder redpackOrder) {
redpackOrder.setTransferType(WxSendredpackType.SENDGROUPREDPACK);
return service.sendredpack(redpackOrder);
}
/**
* 小程序发红包
* @param redpackOrder 红包订单
* @return 结果
*/
public Map<String, Object> sendminiprogramhb(RedpackOrder redpackOrder) {
redpackOrder.setTransferType(WxSendredpackType.SENDMINIPROGRAMHB);
return service.sendredpack(redpackOrder);
}
/**
* 查询红包记录
* 用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包
* 查询红包记录API只支持查询30天内的红包订单30天之前的红包订单请登录商户平台查询。
*
* @param mchBillno 商户发放红包的商户订单号
* @return 返回查询结果
*/
public Map<String, Object> gethbinfo(String mchBillno) {
return service.gethbinfo(mchBillno);
}
}

View File

@@ -2,23 +2,22 @@ package com.egzosn.pay.wx.api;
import com.alibaba.fastjson.JSONObject;
import com.egzosn.pay.common.api.BasePayService;
import com.egzosn.pay.common.api.Callback;
import com.egzosn.pay.common.bean.*;
import com.egzosn.pay.common.bean.result.PayException;
import com.egzosn.pay.common.exception.PayErrorException;
import com.egzosn.pay.common.http.HttpConfigStorage;
import com.egzosn.pay.common.util.DateUtils;
import com.egzosn.pay.common.util.Util;
import com.egzosn.pay.common.util.XML;
import com.egzosn.pay.common.util.sign.SignUtils;
import com.egzosn.pay.common.util.sign.encrypt.RSA2;
import com.egzosn.pay.common.util.str.StringUtils;
import com.egzosn.pay.wx.bean.*;
import com.egzosn.pay.common.util.XML;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.security.GeneralSecurityException;
import java.util.*;
import static com.egzosn.pay.wx.api.WxConst.*;
@@ -29,13 +28,11 @@ import static com.egzosn.pay.wx.bean.WxTransferType.*;
*
* @author egan
* <pre>
* email egzosn@gmail.com
* date 2016-5-18 14:09:01
* </pre>
* email egzosn@gmail.com
* date 2016-5-18 14:09:01
* </pre>
*/
public class WxPayService extends BasePayService<WxPayConfigStorage> {
public class WxPayService extends BasePayService<WxPayConfigStorage> implements WxRedPackService {
/**
@@ -94,15 +91,15 @@ public class WxPayService extends BasePayService<WxPayConfigStorage> {
public boolean verify(Map<String, Object> params) {
if (!(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE)))) {
if (LOG.isErrorEnabled()){
LOG.error(String.format("微信支付异常return_code=%s,参数集=%s", params.get(RETURN_CODE), params));
if (LOG.isErrorEnabled()) {
LOG.error(String.format("微信支付异常return_code=%s,参数集=%s", params.get(RETURN_CODE), params));
}
return false;
}
if (null == params.get(SIGN)) {
if (LOG.isDebugEnabled()){
LOG.debug(String.format("微信支付异常:签名为空!%s=%s", OUT_TRADE_NO, params.get(OUT_TRADE_NO)));
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("微信支付异常:签名为空!%s=%s", OUT_TRADE_NO, params.get(OUT_TRADE_NO)));
}
return false;
}
@@ -243,24 +240,24 @@ public class WxPayService extends BasePayService<WxPayConfigStorage> {
if (verify(preOrderHandler(result, order))) {
//如果是扫码支付或者刷卡付无需处理,直接返回
if (((WxTransactionType) order.getTransactionType()).isReturn()) {
return result;
return result;
}
Map<String, Object> params = new TreeMap<String, Object>();
if (WxTransactionType.JSAPI == order.getTransactionType()) {
params.put("signType", payConfigStorage.getSignType());
params.put("appId", payConfigStorage.getAppid());
params.put("timeStamp", System.currentTimeMillis() / 1000);
params.put("nonceStr", result.get(NONCE_STR));
params.put("package", "prepay_id=" + result.get("prepay_id"));
params.put("signType", payConfigStorage.getSignType());
params.put("appId", payConfigStorage.getAppid());
params.put("timeStamp", System.currentTimeMillis() / 1000);
params.put("nonceStr", result.get(NONCE_STR));
params.put("package", "prepay_id=" + result.get("prepay_id"));
} else if (WxTransactionType.APP == order.getTransactionType()) {
params.put("partnerid", payConfigStorage.getPid());
params.put(APPID, payConfigStorage.getAppid());
params.put("prepayid", result.get("prepay_id"));
params.put("timestamp", System.currentTimeMillis() / 1000);
params.put("noncestr", result.get(NONCE_STR));
params.put("package", "Sign=WXPay");
params.put("partnerid", payConfigStorage.getPid());
params.put(APPID, payConfigStorage.getAppid());
params.put("prepayid", result.get("prepay_id"));
params.put("timestamp", System.currentTimeMillis() / 1000);
params.put("noncestr", result.get(NONCE_STR));
params.put("package", "Sign=WXPay");
}
String paySign = createSign(SignUtils.parameterText(params), payConfigStorage.getInputCharset());
params.put(SIGN, paySign);
@@ -480,23 +477,6 @@ public class WxPayService extends BasePayService<WxPayConfigStorage> {
return secondaryInterface(transactionId, outTradeNo, WxTransactionType.REVERSE);
}
/**
* 退款
*
* @param transactionId 微信订单号
* @param outTradeNo 商户单号
* @param refundAmount 退款金额
* @param totalAmount 总金额
* @return 返回支付方申请退款后的结果
* @see #refund(RefundOrder, Callback)
*/
@Deprecated
@Override
public Map<String, Object> refund(String transactionId, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount) {
return refund(new RefundOrder(transactionId, outTradeNo, refundAmount, totalAmount));
}
@@ -527,18 +507,6 @@ public class WxPayService extends BasePayService<WxPayConfigStorage> {
}
/**
* 查询退款
*
* @param transactionId 支付平台订单号
* @param outTradeNo 商户单号
* @return 返回支付方查询退款后的结果
*/
@Override
public Map<String, Object> refundquery(String transactionId, String outTradeNo) {
return secondaryInterface(transactionId, outTradeNo, WxTransactionType.REFUNDQUERY);
}
/**
* 查询退款
*
@@ -607,7 +575,7 @@ public class WxPayService extends BasePayService<WxPayConfigStorage> {
if (transactionType == WxTransactionType.DOWNLOADBILL) {
if (transactionIdOrBillDate instanceof Date) {
return downloadbill((Date) transactionIdOrBillDate, outTradeNoBillType);
return downloadbill((Date) transactionIdOrBillDate, outTradeNoBillType);
}
throw new PayErrorException(new PayException(FAILURE, "非法类型异常:" + transactionIdOrBillDate.getClass()));
}
@@ -631,13 +599,12 @@ public class WxPayService extends BasePayService<WxPayConfigStorage> {
* @param order 转账订单
* <pre>
*
* 注意事项:
* ◆ 当返回错误码为“SYSTEMERROR”时请不要更换商户订单号一定要使用原商户订单号重试否则可能造成重复支付等资金风险。
* ◆ XML具有可扩展性因此返回参数可能会有新增而且顺序可能不完全遵循此文档规范如果在解析回包的时候发生错误请商户务必不要换单重试请商户联系客服确认付款情况。如果有新回包字段会更新到此API文档中。
* ◆ 因为错误代码字段err_code的值后续可能会增加所以商户如果遇到回包返回新的错误码请商户务必不要换单重试请商户联系客服确认付款情况。如果有新的错误码会更新到此API文档中。
* ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考系统实现时请不要依赖这个字段来做自动化处理。
*
* </pre>
*注意事项:
*◆ 当返回错误码为“SYSTEMERROR”时请不要更换商户订单号一定要使用原商户订单号重试否则可能造成重复支付等资金风险。
*◆ XML具有可扩展性因此返回参数可能会有新增而且顺序可能不完全遵循此文档规范如果在解析回包的时候发生错误请商户务必不要换单重试请商户联系客服确认付款情况。如果有新回包字段会更新到此API文档中。
*◆ 因为错误代码字段err_code的值后续可能会增加所以商户如果遇到回包返回新的错误码请商户务必不要换单重试请商户联系客服确认付款情况。如果有新的错误码会更新到此API文档中。
*◆ 错误代码描述字段err_code_des只供人工定位问题时做参考系统实现时请不要依赖这个字段来做自动化处理。
*</pre>
* @return 对应的转账结果
*/
@Override
@@ -711,11 +678,11 @@ public class WxPayService extends BasePayService<WxPayConfigStorage> {
*
* @param outNo 商户转账订单号
* @param wxTransferType 微信转账类型,.....这里没办法了只能这样写(┬_┬),请见谅 {@link com.egzosn.pay.wx.bean.WxTransferType}
* <p>
* <p>
* <a href="https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_3">企业付款到零钱</a>
* <a href="https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_3">商户企业付款到银行卡</a>
* </p>
* <p>
* <p>
* <a href="https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_3">企业付款到零钱</a>
* <a href="https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_3">商户企业付款到银行卡</a>
* </p>
* @return 对应的转账订单
*/
@Override
@@ -740,7 +707,7 @@ public class WxPayService extends BasePayService<WxPayConfigStorage> {
public String keyPublic(String content) {
try {
return RSA2.encrypt(content, payConfigStorage.getKeyPublic(), CIPHER_ALGORITHM, payConfigStorage.getInputCharset());
} catch (Exception e) {
} catch (GeneralSecurityException | IOException e) {
throw new PayErrorException(new WxPayError(FAILURE, e.getLocalizedMessage()));
}
}
@@ -758,70 +725,51 @@ public class WxPayService extends BasePayService<WxPayConfigStorage> {
/**
* 微信发红包
* @author: faymanwang 1057438332@qq.com
*
* @param redpackOrder 红包实体
* @return 返回发红包实体后的结果
* @author: faymanwang 1057438332@qq.com
*/
@Override
public Map<String, Object> sendredpack(RedpackOrder redpackOrder) {
Map<String, Object> parameters = new TreeMap<String, Object>();
redpackParam(redpackOrder, parameters);
redPackParam(redpackOrder, parameters);
if (WxSendredpackType.SENDGROUPREDPACK == redpackOrder.getTransferType()) {
parameters.put("amt_type", "ALL_RAND");
} else if (WxSendredpackType.SENDMINIPROGRAMHB == redpackOrder.getTransferType()) {
parameters.put("notify_way", "MINI_PROGRAM_JSAPI");
}
parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset()));
return requestTemplate.postForObject(getReqUrl( WxSendredpackType.SENDREDPACK), XML.getMap2Xml(parameters) , JSONObject.class);
return requestTemplate.postForObject(getReqUrl(redpackOrder.getTransferType()), XML.getMap2Xml(parameters), JSONObject.class);
}
/**
* 发放裂变红包
* 裂变红包:一次可以发放一组红包。首先领取的用户为种子用户,种子用户领取一组红包当中的一个,并可以通过社交分享将剩下的红包给其他用户。裂变红包充分利用了人际传播的优势。
*
* @author: faymanwang 1057438332@qq.com
* @param redpackOrder 红包实体
* @return 返回发红包实体后的结果
*/
public Map<String, Object> sendgroupredpack(RedpackOrder redpackOrder) {
Map<String, Object> parameters = new TreeMap<String, Object>();
redpackParam(redpackOrder, parameters);
parameters.put("amt_type", "ALL_RAND");
parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset()));
return requestTemplate.postForObject(getReqUrl( WxSendredpackType.SENDGROUPREDPACK), XML.getMap2Xml(parameters) , JSONObject.class);
}
/**
* 小程序发红包
* @author: faymanwang 1057438332@qq.com
* @param redpackOrder 红包实体
* @return 返回发红包实体后的结果
*/
public Map<String, Object> sendminiprogramhb(RedpackOrder redpackOrder) {
Map<String, Object> parameters = new TreeMap<String, Object>();
redpackParam(redpackOrder, parameters);
parameters.put("notify_way", "MINI_PROGRAM_JSAPI");
parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset()));
return requestTemplate.postForObject(getReqUrl( WxSendredpackType.SENDMINIPROGRAMHB), XML.getMap2Xml(parameters) , JSONObject.class);
}
/**
* 查询红包记录
* 用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包
* 查询红包记录API只支持查询30天内的红包订单30天之前的红包订单请登录商户平台查询。
*
* @author: faymanwang 1057438332@qq.com
* @param mchBillno 商户发放红包的商户订单号
* @return 返回查询结果
* @author: faymanwang 1057438332@qq.com
*/
public Map<String, Object> gethbinfo (String mchBillno) {
@Override
public Map<String, Object> gethbinfo(String mchBillno) {
Map<String, Object> parameters = this.getPublicParameters();
parameters.put("mch_billno", mchBillno);
parameters.put("bill_type", "MCHT");
parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset()));
return requestTemplate.postForObject(getReqUrl( WxSendredpackType.GETHBINFO), XML.getMap2Xml(parameters) , JSONObject.class);
return requestTemplate.postForObject(getReqUrl(WxSendredpackType.GETHBINFO), XML.getMap2Xml(parameters), JSONObject.class);
}
/**
* 微信红包构造参数方法
*
* @param redpackOrder 红包实体
* @param parameters
* @param parameters 接收参数
*/
private void redpackParam(RedpackOrder redpackOrder, Map<String, Object> parameters) {
private void redPackParam(RedpackOrder redpackOrder, Map<String, Object> parameters) {
parameters.put(NONCE_STR, SignUtils.randomStr());
parameters.put(MCH_ID, payConfigStorage.getPid());
parameters.put("wxappid", payConfigStorage.getAppid());
@@ -829,12 +777,12 @@ public class WxPayService extends BasePayService<WxPayConfigStorage> {
parameters.put("re_openid", redpackOrder.getReOpenid());
parameters.put("mch_billno", redpackOrder.getMchBillno());
parameters.put("total_amount", Util.conversionCentAmount(redpackOrder.getTotalAmount()));
parameters.put("total_num", redpackOrder.getTotalNum() > 0 ? redpackOrder.getTotalNum() : 1);
parameters.put("total_num", Math.max(redpackOrder.getTotalNum(), 1));
parameters.put("wishing", redpackOrder.getWishing());
parameters.put("client_ip", StringUtils.isNotEmpty(redpackOrder.getIp()) ? redpackOrder.getIp() : "192.168.0.1");
parameters.put("act_name", redpackOrder.getActName());
parameters.put("remark", redpackOrder.getRemark());
if(StringUtils.isNotEmpty(redpackOrder.getSceneId())){
if (StringUtils.isNotEmpty(redpackOrder.getSceneId())) {
parameters.put("scene_id", redpackOrder.getSceneId());
}
}

View File

@@ -0,0 +1,33 @@
package com.egzosn.pay.wx.api;
import com.egzosn.pay.wx.bean.RedpackOrder;
import java.util.Map;
/**
* 微信红包服务
* @author egan
* <pre>
* email egzosn@gmail.com
* date 2020/5/17 22:24
* </pre>
*/
public interface WxRedPackService {
/**
* 微信发红包
*
* @param redpackOrder 红包实体
* @return 返回发红包实体后的结果
*/
Map<String, Object> sendredpack(RedpackOrder redpackOrder);
/**
* 查询红包记录
* 用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包
* 查询红包记录API只支持查询30天内的红包订单30天之前的红包订单请登录商户平台查询。
*
* @param mchBillno 商户发放红包的商户订单号
* @return 返回查询结果
*/
Map<String, Object> gethbinfo(String mchBillno);
}

View File

@@ -1,61 +1,105 @@
package com.egzosn.pay.wx.bean;
import com.egzosn.pay.common.bean.TransferOrder;
import java.math.BigDecimal;
/**
* @description:
* @description: 发红包订单
* @author: 保网 faymanwang 1057438332@qq.com
* @time: 2020/5/15 12:40
*/
public class RedpackOrder {
public class RedpackOrder extends TransferOrder {
/**
* 商户订单号每个订单号必须唯一。取值范围0~9a~zA~Z
* 接口根据商户订单号支持重入,如出现超时可再调用
* @return 商户订单号
*/
private String mchBillno;
public String getMchBillno() {
return getOutNo();
}
public void setMchBillno(String mchBillno) {
setOutNo(mchBillno);
}
/**
* 商户名称:红包发送者名称
* @return 红包发送者名称
*/
private String sendName;
public String getSendName() {
return getPayerName();
}
public void setSendName(String sendName) {
super.setPayerName(sendName);
}
/**
* 用户openid
* @return 用户openid
*/
private String reOpenid;
public String getReOpenid() {
return getPayeeAccount();
}
public void setReOpenid(String reOpenid) {
super.setPayeeAccount(reOpenid);
}
/**
* 付款金额 每个红包金额必须在默认额度内默认大于1元小于200元可在产品设置中自行申请调高额度
* @return 付款金额
*/
private BigDecimal totalAmount;
public BigDecimal getTotalAmount() {
return getAmount();
}
public void setTotalAmount(BigDecimal totalAmount) {
super.setAmount(totalAmount);
}
/**
* 红包发放总人数
* 普通红包1
* 裂变:必须介于(包括)3到20之间
*/
private int totalNum;
public int getTotalNum() {
Object totalNum = getAttr("total_num");
return null == totalNum ? 1 : (Integer)totalNum;
}
public void setTotalNum(int totalNum) {
addAttr("total_num", totalNum);
}
/**
* 红包祝福语
*/
private String wishing;
public String getWishing() {
return (String) getAttr("wishing");
}
public void setWishing(String wishing) {
addAttr("wishing", wishing);
}
/**
* 操作者ip根据支付平台所需进行设置
*/
private String ip;
/**
* 活动名称
*/
private String actName;
public String getActName() {
return (String) getAttr("act_name");
}
/**
* 备注
*/
private String remark;
public void setActName(String actName) {
addAttr("act_name", actName);
}
public String getSceneId() {
return (String) getAttr("scene_id");
}
/**
* 发放红包使用场景红包金额大于200或者小于1元时必传
* PRODUCT_1:商品促销
@@ -67,86 +111,12 @@ public class RedpackOrder {
* PRODUCT_7:彩票派奖
* PRODUCT_8:税务刮奖
*/
private String sceneId;
public String getMchBillno() {
return mchBillno;
}
public void setMchBillno(String mchBillno) {
this.mchBillno = mchBillno;
}
public String getSendName() {
return sendName;
}
public void setSendName(String sendName) {
this.sendName = sendName;
}
public String getReOpenid() {
return reOpenid;
}
public void setReOpenid(String reOpenid) {
this.reOpenid = reOpenid;
}
public BigDecimal getTotalAmount() {
return totalAmount;
}
public void setTotalAmount(BigDecimal totalAmount) {
this.totalAmount = totalAmount;
}
public int getTotalNum() {
return totalNum;
}
public void setTotalNum(int totalNum) {
this.totalNum = totalNum;
}
public String getWishing() {
return wishing;
}
public void setWishing(String wishing) {
this.wishing = wishing;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getActName() {
return actName;
}
public void setActName(String actName) {
this.actName = actName;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getSceneId() {
return sceneId;
}
public void setSceneId(String sceneId) {
this.sceneId = sceneId;
addAttr("scene_id", sceneId);
}
public void setTransferType(WxSendredpackType transferType) {
super.setTransferType(transferType);
}
}

View File

@@ -3,7 +3,7 @@ package com.egzosn.pay.wx.bean;
import com.egzosn.pay.common.bean.TransferType;
/**
* @description:
* @description: 红包交易类型
* @author: faymanwang
* @time: 2020/5/14 20:11
*/