增加小新支付通道接口

This commit is contained in:
dingzhiwei 2021-09-21 01:29:44 +08:00
parent ecc4bd10bb
commit a211251161
8 changed files with 555 additions and 4 deletions

View File

@ -137,10 +137,10 @@ public class CS {
//接口类型
public interface IF_CODE{
String ALIPAY = "alipay"; //支付宝官方支付
String WXPAY = "wxpay"; //微信官方支付
String YSFPAY = "ysfpay"; //云闪付开放平台
String ALIPAY = "alipay"; // 支付宝官方支付
String WXPAY = "wxpay"; // 微信官方支付
String YSFPAY = "ysfpay"; // 云闪付开放平台
String XXPAY = "xxpay"; // 小新支付
}

View File

@ -19,6 +19,7 @@ import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.model.params.alipay.AlipayNormalMchParams;
import com.jeequan.jeepay.core.model.params.wxpay.WxpayNormalMchParams;
import com.jeequan.jeepay.core.model.params.xxpay.XxpayNormalMchParams;
/*
* 抽象类 普通商户参数定义
@ -35,6 +36,8 @@ public abstract class NormalMchParams {
return JSONObject.parseObject(paramsStr, WxpayNormalMchParams.class);
}else if(CS.IF_CODE.ALIPAY.equals(ifCode)){
return JSONObject.parseObject(paramsStr, AlipayNormalMchParams.class);
}else if(CS.IF_CODE.XXPAY.equals(ifCode)){
return JSONObject.parseObject(paramsStr, XxpayNormalMchParams.class);
}
return null;
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeequan.jeepay.core.model.params.xxpay;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.model.params.NormalMchParams;
import com.jeequan.jeepay.core.utils.StringKit;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
/*
* 小新支付 普通商户参数定义
*
* @author jmdhappy
* @site https://www.jeequan.com
* @date 2021/9/10 21:51
*/
@Data
public class XxpayNormalMchParams extends NormalMchParams {
/** 商户号 */
private String mchId;
/** 私钥 */
private String key;
/** 支付网关地址 */
private String payUrl;
@Override
public String deSenData() {
XxpayNormalMchParams mchParams = this;
if (StringUtils.isNotBlank(this.key)) {
mchParams.setKey(StringKit.str2Star(this.key, 4, 4, 6));
}
return ((JSONObject) JSON.toJSON(mchParams)).toJSONString();
}
}

View File

@ -0,0 +1,104 @@
/*
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeequan.jeepay.pay.channel.xxpay;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.ResponseException;
import com.jeequan.jeepay.core.model.params.xxpay.XxpayNormalMchParams;
import com.jeequan.jeepay.pay.channel.AbstractChannelNoticeService;
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
/*
* 小新支付 回调接口实现类
*
* @author jmdhappy
* @site https://www.jeequan.com
* @date 2021/9/21 00:16
*/
@Service
@Slf4j
public class XxpayChannelNoticeService extends AbstractChannelNoticeService {
@Override
public String getIfCode() {
return CS.IF_CODE.XXPAY;
}
@Override
public MutablePair<String, Object> parseParams(HttpServletRequest request, String urlOrderId, NoticeTypeEnum noticeTypeEnum) {
try {
JSONObject params = getReqParamJSON();
String payOrderId = params.getString("mchOrderNo");
return MutablePair.of(payOrderId, params);
} catch (Exception e) {
log.error("error", e);
throw ResponseException.buildText("ERROR");
}
}
@Override
public ChannelRetMsg doNotice(HttpServletRequest request, Object params, PayOrder payOrder, MchAppConfigContext mchAppConfigContext, NoticeTypeEnum noticeTypeEnum) {
try {
XxpayNormalMchParams xxpayParams = mchAppConfigContext.getNormalMchParamsByIfCode(getIfCode(), XxpayNormalMchParams.class);
// 获取请求参数
JSONObject jsonParams = (JSONObject) params;
String checkSign = jsonParams.getString("sign");
jsonParams.remove("sign");
// 验证签名
if(!checkSign.equals(XxpayKit.getSign(jsonParams, xxpayParams.getKey()))) {
throw ResponseException.buildText("ERROR");
}
//验签成功后判断上游订单状态
ResponseEntity okResponse = textResp("success");
// 支付状态,0-订单生成,1-支付中,2-支付成功,3-业务处理完成
String status = jsonParams.getString("status");
ChannelRetMsg result = new ChannelRetMsg();
result.setChannelOrderId(jsonParams.getString("channelOrderNo")); //渠道订单号
result.setResponseEntity(okResponse); //响应数据
result.setChannelState(ChannelRetMsg.ChannelState.WAITING); // 默认支付中
if("2".equals(status) || "3".equals(status)){
result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
}
return result;
} catch (Exception e) {
log.error("error", e);
throw ResponseException.buildText("ERROR");
}
}
}

View File

@ -0,0 +1,115 @@
package com.jeequan.jeepay.pay.channel.xxpay;
import org.apache.commons.lang3.StringUtils;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
/*
* 小新支付 工具类
*
* @author jmdhappy
* @site https://www.jeequan.com
* @date 2021/9/20 10:09
*/
public class XxpayKit {
private static String encodingCharset = "UTF-8";
/**
* 计算签名
* @param map
* @param key
* @return
*/
public static String getSign(Map<String,Object> map, String key){
ArrayList<String> list = new ArrayList<String>();
for(Map.Entry<String,Object> entry:map.entrySet()){
if(null != entry.getValue() && !"".equals(entry.getValue())){
list.add(entry.getKey() + "=" + entry.getValue() + "&");
}
}
int size = list.size();
String [] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < size; i ++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += "key=" + key;
result = md5(result, encodingCharset).toUpperCase();
return result;
}
/**
* 计算MD5值
* @param value
* @param charset
* @return
*/
public static String md5(String value, String charset) {
MessageDigest md;
try {
byte[] data = value.getBytes(charset);
md = MessageDigest.getInstance("MD5");
byte[] digestData = md.digest(data);
return toHex(digestData);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
public static String toHex(byte input[]) {
if (input == null) {
return null;
}
StringBuffer output = new StringBuffer(input.length * 2);
for (int i = 0; i < input.length; i++) {
int current = input[i] & 0xff;
if (current < 16){
output.append("0");
}
output.append(Integer.toString(current, 16));
}
return output.toString();
}
public static String getPaymentUrl(String payUrl) {
return getPayUrl(payUrl) + "api/pay/create_order";
}
public static String getQueryPayOrderUrl(String payUrl) {
return getPayUrl(payUrl) + "api/pay/query_order";
}
public static String getRefundUrl(String payUrl) {
return getPayUrl(payUrl) + "api/refund/create_order";
}
public static String getQueryRefundOrderUrl(String payUrl) {
return getPayUrl(payUrl) + "api/refund/query_order";
}
protected static String getPayUrl(String payUrl) {
if(StringUtils.isEmpty(payUrl)) {
return payUrl;
}
if(!payUrl.endsWith("/")) {
payUrl += "/";
}
return payUrl;
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeequan.jeepay.pay.channel.xxpay;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.model.params.xxpay.XxpayNormalMchParams;
import com.jeequan.jeepay.core.utils.JeepayKit;
import com.jeequan.jeepay.pay.channel.IPayOrderQueryService;
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.TreeMap;
/*
* 小新支付 查单接口实现类
*
* @author jmdhappy
* @site https://www.jeequan.com
* @date 2021/9/21 01:05
*/
@Service
@Slf4j
public class XxpayPayOrderQueryService implements IPayOrderQueryService {
@Override
public String getIfCode() {
return CS.IF_CODE.XXPAY;
}
@Override
public ChannelRetMsg query(PayOrder payOrder, MchAppConfigContext mchAppConfigContext){
XxpayNormalMchParams xxpayParams = mchAppConfigContext.getNormalMchParamsByIfCode(getIfCode(), XxpayNormalMchParams.class);
String queryPayOrderUrl = XxpayKit.getQueryPayOrderUrl(xxpayParams.getPayUrl());
Map<String,Object> paramMap = new TreeMap();
// 接口类型
paramMap.put("mchId", xxpayParams.getMchId());
paramMap.put("mchOrderNo", payOrder.getPayOrderId());
String sign = XxpayKit.getSign(paramMap, xxpayParams.getKey());
paramMap.put("sign", sign);
String resStr = "";
try {
resStr = HttpUtil.createPost(queryPayOrderUrl + "?" + JeepayKit.genUrlParams(paramMap)).timeout(60 * 1000).execute().body();
} catch (Exception e) {
log.error("http error", e);
}
if(StringUtils.isEmpty(resStr)) {
return ChannelRetMsg.waiting(); //支付中
}
JSONObject resObj = JSONObject.parseObject(resStr);
if(!"0".equals(resObj.getString("retCode"))){
return ChannelRetMsg.waiting(); //支付中
}
// 支付状态,0-订单生成,1-支付中,2-支付成功,3-业务处理完成
String status = resObj.getString("status");
if("2".equals(status) || "3".equals(status)) {
return ChannelRetMsg.confirmSuccess(resObj.getString("channelOrderNo")); //支付成功
}
return ChannelRetMsg.waiting(); //支付中
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeequan.jeepay.pay.channel.xxpay;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.pay.channel.AbstractPaymentService;
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
import com.jeequan.jeepay.pay.rqrs.AbstractRS;
import com.jeequan.jeepay.pay.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.pay.util.PaywayUtil;
import org.springframework.stereotype.Service;
/*
* 支付接口 小新支付
* 支付方式 自适应
*
* @author jmdhappy
* @site https://www.jeequan.com
* @date 2021/9/20 20:00
*/
@Service
public class XxpayPaymentService extends AbstractPaymentService {
@Override
public String getIfCode() {
return CS.IF_CODE.XXPAY;
}
@Override
public boolean isSupport(String wayCode) {
return true;
}
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
return PaywayUtil.getRealPaywayService(this, payOrder.getWayCode()).preCheck(rq, payOrder);
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception {
return PaywayUtil.getRealPaywayService(this, payOrder.getWayCode()).pay(rq, payOrder, mchAppConfigContext);
}
}

View File

@ -0,0 +1,136 @@
/*
* Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeequan.jeepay.pay.channel.xxpay.payway;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.entity.PayOrder;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.params.xxpay.XxpayNormalMchParams;
import com.jeequan.jeepay.core.utils.JeepayKit;
import com.jeequan.jeepay.pay.channel.xxpay.XxpayKit;
import com.jeequan.jeepay.pay.channel.xxpay.XxpayPaymentService;
import com.jeequan.jeepay.pay.model.MchAppConfigContext;
import com.jeequan.jeepay.pay.rqrs.AbstractRS;
import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.pay.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.pay.rqrs.payorder.payway.AliBarOrderRQ;
import com.jeequan.jeepay.pay.rqrs.payorder.payway.AliBarOrderRS;
import com.jeequan.jeepay.pay.util.ApiResBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.TreeMap;
/*
* 小新支付 支付宝条码支付
*
* @author jmdhappy
* @site https://www.jeequan.com
* @date 2021/9/20 10:09
*/
@Service("xxpayPaymentByAliBarService") //Service Name需保持全局唯一性
@Slf4j
public class AliBar extends XxpayPaymentService {
@Override
public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) {
AliBarOrderRQ bizRQ = (AliBarOrderRQ) rq;
if(StringUtils.isEmpty(bizRQ.getAuthCode())){
throw new BizException("用户支付条码[authCode]不可为空");
}
return null;
}
@Override
public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext){
AliBarOrderRQ bizRQ = (AliBarOrderRQ) rq;
XxpayNormalMchParams params = mchAppConfigContext.getNormalMchParamsByIfCode(getIfCode(), XxpayNormalMchParams.class);
Map<String,Object> paramMap = new TreeMap();
// 接口类型
paramMap.put("mchId", params.getMchId());
paramMap.put("productId", "8021"); // 支付宝条码
paramMap.put("mchOrderNo", payOrder.getPayOrderId());
paramMap.put("amount", payOrder.getAmount() + "");
paramMap.put("currency", "cny");
paramMap.put("clientIp", payOrder.getClientIp());
paramMap.put("device", "web");
paramMap.put("returnUrl", getReturnUrl());
paramMap.put("notifyUrl", getNotifyUrl(payOrder.getPayOrderId()));
paramMap.put("subject", payOrder.getSubject());
paramMap.put("body", payOrder.getBody());
paramMap.put("extra", bizRQ.getAuthCode());
String sign = XxpayKit.getSign(paramMap, params.getKey());
paramMap.put("sign", sign);
// 构造函数响应数据
AliBarOrderRS res = ApiResBuilder.buildSuccess(AliBarOrderRS.class);
ChannelRetMsg channelRetMsg = new ChannelRetMsg();
res.setChannelRetMsg(channelRetMsg);
// 设置支付下单地址
String payUrl = XxpayKit.getPaymentUrl(params.getPayUrl());
String resStr = "";
try {
resStr = HttpUtil.createPost(payUrl + "?" + JeepayKit.genUrlParams(paramMap)).timeout(60 * 1000).execute().body();
} catch (Exception e) {
log.error("http error", e);
}
if(StringUtils.isEmpty(resStr)) {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrCode("");
channelRetMsg.setChannelErrMsg("请求"+getIfCode()+"接口异常");
return res;
}
JSONObject resObj = JSONObject.parseObject(resStr);
if(!"0".equals(resObj.getString("retCode"))){
String retMsg = resObj.getString("retMsg");
log.error("请求"+getIfCode()+"返回结果异常, resObj={}", resObj.toJSONString());
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
channelRetMsg.setChannelErrCode("");
channelRetMsg.setChannelErrMsg(retMsg);
return res;
}
// 验证响应数据签名
String checkSign = resObj.getString("sign");
resObj.remove("sign");
if(!checkSign.equals(XxpayKit.getSign(resObj, params.getKey()))) {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL);
return res;
}
// 订单状态-2:订单已关闭,0-订单生成,1-支付中,2-支付成功,3-业务处理完成,4-已退款
String orderStatus = resObj.getString("orderStatus");
if("2".equals(orderStatus) || "3".equals(orderStatus)) {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS);
}else {
channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING);
}
return res;
}
}