From 2743de4b652debfba7d8491803f980bdd5bfb6c9 Mon Sep 17 00:00:00 2001 From: yurong Date: Tue, 23 Aug 2022 10:40:10 +0800 Subject: [PATCH] =?UTF-8?q?jeepay=E5=AF=B9=E6=8E=A5=E8=AE=A1=E5=85=A8?= =?UTF-8?q?=E4=BB=98+=E8=AE=A1=E5=85=A8=E4=BB=98=E9=85=8D=E7=BD=AESQL+?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0pls=E5=8C=85+=E5=BE=AE=E4=BF=A1=E5=92=8C?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=9A=84?= =?UTF-8?q?OrderRQ=E5=92=8COrderRS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/sql/init.sql | 10 ++ docs/sql/patch.sql | 12 +- .../com/jeequan/jeepay/core/constants/CS.java | 2 + .../core/model/params/NormalMchParams.java | 3 + .../params/jeepluspay/JeepluspayConfig.java | 55 +++++++ .../jeepluspay/JeepluspayNormalMchParams.java | 64 ++++++++ jeepay-payment/pom.xml | 15 ++ .../JeepluspayChannelNoticeService.java | 128 +++++++++++++++ .../JeepluspayChannelRefundNoticeService.java | 133 +++++++++++++++ .../JeepluspayPayOrderQueryService.java | 98 +++++++++++ .../jeepluspay/JeepluspayPaymentService.java | 41 +++++ .../jeepluspay/JeepluspayRefundService.java | 154 ++++++++++++++++++ .../pay/channel/jeepluspay/payway/AliApp.java | 115 +++++++++++++ .../pay/channel/jeepluspay/payway/AliBar.java | 119 ++++++++++++++ .../channel/jeepluspay/payway/AliJsapi.java | 122 ++++++++++++++ .../channel/jeepluspay/payway/AliLite.java | 122 ++++++++++++++ .../pay/channel/jeepluspay/payway/AliPc.java | 117 +++++++++++++ .../pay/channel/jeepluspay/payway/AliQr.java | 116 +++++++++++++ .../pay/channel/jeepluspay/payway/AliWap.java | 117 +++++++++++++ .../pay/channel/jeepluspay/payway/WxApp.java | 116 +++++++++++++ .../pay/channel/jeepluspay/payway/WxBar.java | 119 ++++++++++++++ .../pay/channel/jeepluspay/payway/WxH5.java | 118 ++++++++++++++ .../channel/jeepluspay/payway/WxJsapi.java | 123 ++++++++++++++ .../pay/channel/jeepluspay/payway/WxLite.java | 123 ++++++++++++++ .../channel/jeepluspay/payway/WxNative.java | 116 +++++++++++++ .../pay/channel/xxpay/payway/WxBar.java | 4 +- .../pay/rqrs/payorder/UnifiedOrderRQ.java | 8 + .../rqrs/payorder/payway/AliLiteOrderRQ.java | 48 ++++++ .../rqrs/payorder/payway/AliLiteOrderRS.java | 46 ++++++ .../rqrs/payorder/payway/WxLiteOrderRQ.java | 50 ++++++ .../rqrs/payorder/payway/WxLiteOrderRS.java | 45 +++++ libs/jeepay-sdk-java-pls-1.0.0.jar | Bin 0 -> 90726 bytes 32 files changed, 2456 insertions(+), 3 deletions(-) create mode 100644 jeepay-core/src/main/java/com/jeequan/jeepay/core/model/params/jeepluspay/JeepluspayConfig.java create mode 100644 jeepay-core/src/main/java/com/jeequan/jeepay/core/model/params/jeepluspay/JeepluspayNormalMchParams.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayChannelNoticeService.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayChannelRefundNoticeService.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayPayOrderQueryService.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayPaymentService.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayRefundService.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliApp.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliBar.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliJsapi.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliLite.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliPc.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliQr.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliWap.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxApp.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxBar.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxH5.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxJsapi.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxLite.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxNative.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/payway/AliLiteOrderRQ.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/payway/AliLiteOrderRS.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/payway/WxLiteOrderRQ.java create mode 100644 jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/payway/WxLiteOrderRS.java create mode 100644 libs/jeepay-sdk-java-pls-1.0.0.jar diff --git a/docs/sql/init.sql b/docs/sql/init.sql index ffe3090..45b45a6 100644 --- a/docs/sql/init.sql +++ b/docs/sql/init.sql @@ -755,3 +755,13 @@ VALUES ('pppay', 'PayPal支付', 1, 0, 1, '[{"name":"sandbox","desc":"环境配置","type":"radio","verify":"required","values":"1,0","titles":"沙箱环境, 生产环境"},{"name":"clientId","desc":"Client ID(客户端ID)","type":"text","verify":"required"},{"name":"secret","desc":"Secret(密钥)","type":"text","verify":"required","star":"1"},{"name":"refundWebhook","desc":"退款 Webhook id","type":"text","verify":"required"},{"name":"notifyWebhook","desc":"支付 Webhook id","type":"text","verify":"required"}]', '[{"wayCode": "PP_PC"}]', 'http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/paypal.png', '#005ea6', 1, 'PayPal官方通道'); + +-- 计全付支付通道 +INSERT INTO t_pay_interface_define (if_code, if_name, is_mch_mode, is_isv_mode, config_page_type, isv_params, isvsub_mch_params, normal_mch_params, way_codes, icon, bg_color, state, remark) +VALUES ('jeepluspay', '计全付', 1, 0, 1, + NULL, + NULL, + '[{"name":"signType","desc":"签名方式","type":"radio","verify":"required","values":"MD5,RSA2","titles":"MD5,RSA2"},{"name":"merchantNo","desc":"计全付商户号","type":"text","verify":"required"},{"name":"appId","desc":"应用ID","type":"text","verify":"required"},{"name":"appSecret","desc":"md5秘钥","type":"textarea","verify":"required","star":"1"},{"name":"rsa2AppPrivateKey","desc":"RSA2: 应用私钥","type":"text","verify":"required","star":"1"},{"name":"rsa2PayPublicKey","desc":"RSA2: 支付网关公钥","type":"text","verify":"required","star":"1"}]', + '[{"wayCode": "ALI_APP"}, {"wayCode": "ALI_BAR"}, {"wayCode": "ALI_JSAPI"}, {"wayCode": "ALI_LITE"}, {"wayCode": "ALI_PC"}, {"wayCode": "ALI_QR"}, {"wayCode": "ALI_WAP"}, {"wayCode": "WX_APP"}, {"wayCode": "WX_BAR"}, {"wayCode": "WX_H5"}, {"wayCode": "WX_JSAPI"}, {"wayCode": "WX_LITE"}, {"wayCode": "WX_NATIVE"}]', + 'http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/jeepluspay.svg', '#0CACFF', 1, '计全付'); + diff --git a/docs/sql/patch.sql b/docs/sql/patch.sql index 7463b07..b4a6548 100644 --- a/docs/sql/patch.sql +++ b/docs/sql/patch.sql @@ -254,4 +254,14 @@ VALUES ('wxpay', '微信支付官方', 1, 1, 2, ## -- ++++ [v1.13.0] ===> [v1.14.0] ++++ -- 日志请求参数、响应参数长度修改 alter table t_sys_log modify `opt_req_param` TEXT DEFAULT NULL COMMENT '操作请求参数'; -alter table t_sys_log modify `opt_res_info` TEXT DEFAULT NULL COMMENT '操作响应结果'; \ No newline at end of file +alter table t_sys_log modify `opt_res_info` TEXT DEFAULT NULL COMMENT '操作响应结果'; + + +-- 增加计全付支付通道 +INSERT INTO t_pay_interface_define (if_code, if_name, is_mch_mode, is_isv_mode, config_page_type, isv_params, isvsub_mch_params, normal_mch_params, way_codes, icon, bg_color, state, remark) +VALUES ('jeepluspay', '计全付', 1, 0, 1, + NULL, + NULL, + '[{"name":"signType","desc":"签名方式","type":"radio","verify":"required","values":"MD5,RSA2","titles":"MD5,RSA2"},{"name":"merchantNo","desc":"计全付商户号","type":"text","verify":"required"},{"name":"appId","desc":"应用ID","type":"text","verify":"required"},{"name":"appSecret","desc":"md5秘钥","type":"textarea","verify":"required","star":"1"},{"name":"rsa2AppPrivateKey","desc":"RSA2: 应用私钥","type":"text","verify":"required","star":"1"},{"name":"rsa2PayPublicKey","desc":"RSA2: 支付网关公钥","type":"text","verify":"required","star":"1"}]', + '[{"wayCode": "ALI_APP"}, {"wayCode": "ALI_BAR"}, {"wayCode": "ALI_JSAPI"}, {"wayCode": "ALI_LITE"}, {"wayCode": "ALI_PC"}, {"wayCode": "ALI_QR"}, {"wayCode": "ALI_WAP"}, {"wayCode": "WX_APP"}, {"wayCode": "WX_BAR"}, {"wayCode": "WX_H5"}, {"wayCode": "WX_JSAPI"}, {"wayCode": "WX_LITE"}, {"wayCode": "WX_NATIVE"}]', + 'http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/jeepluspay.svg', '#0CACFF', 1, '计全付'); diff --git a/jeepay-core/src/main/java/com/jeequan/jeepay/core/constants/CS.java b/jeepay-core/src/main/java/com/jeequan/jeepay/core/constants/CS.java index a783606..89d7386 100644 --- a/jeepay-core/src/main/java/com/jeequan/jeepay/core/constants/CS.java +++ b/jeepay-core/src/main/java/com/jeequan/jeepay/core/constants/CS.java @@ -147,6 +147,7 @@ public class CS { String YSFPAY = "ysfpay"; // 云闪付开放平台 String XXPAY = "xxpay"; // 小新支付 String PPPAY = "pppay"; // Paypal 支付 + String JEEPLUSPAY = "jeepluspay"; // 计全支付plus } @@ -159,6 +160,7 @@ public class CS { String ALI_BAR = "ALI_BAR"; //支付宝条码支付 String ALI_JSAPI = "ALI_JSAPI"; //支付宝服务窗支付 + String ALI_LITE = "ALI_LITE"; //支付宝小程序支付 String ALI_APP = "ALI_APP"; //支付宝 app支付 String ALI_PC = "ALI_PC"; //支付宝 电脑网站支付 String ALI_WAP = "ALI_WAP"; //支付宝 wap支付 diff --git a/jeepay-core/src/main/java/com/jeequan/jeepay/core/model/params/NormalMchParams.java b/jeepay-core/src/main/java/com/jeequan/jeepay/core/model/params/NormalMchParams.java index 0c0d9c3..c1295f6 100644 --- a/jeepay-core/src/main/java/com/jeequan/jeepay/core/model/params/NormalMchParams.java +++ b/jeepay-core/src/main/java/com/jeequan/jeepay/core/model/params/NormalMchParams.java @@ -18,6 +18,7 @@ package com.jeequan.jeepay.core.model.params; 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.jeepluspay.JeepluspayNormalMchParams; import com.jeequan.jeepay.core.model.params.pppay.PpPayNormalMchParams; import com.jeequan.jeepay.core.model.params.wxpay.WxpayNormalMchParams; import com.jeequan.jeepay.core.model.params.xxpay.XxpayNormalMchParams; @@ -41,6 +42,8 @@ public abstract class NormalMchParams { return JSONObject.parseObject(paramsStr, XxpayNormalMchParams.class); }else if (CS.IF_CODE.PPPAY.equals(ifCode)){ return JSONObject.parseObject(paramsStr, PpPayNormalMchParams.class); + }else if (CS.IF_CODE.JEEPLUSPAY.equals(ifCode)){ + return JSONObject.parseObject(paramsStr, JeepluspayNormalMchParams.class); } return null; } diff --git a/jeepay-core/src/main/java/com/jeequan/jeepay/core/model/params/jeepluspay/JeepluspayConfig.java b/jeepay-core/src/main/java/com/jeequan/jeepay/core/model/params/jeepluspay/JeepluspayConfig.java new file mode 100644 index 0000000..c88ef7e --- /dev/null +++ b/jeepay-core/src/main/java/com/jeequan/jeepay/core/model/params/jeepluspay/JeepluspayConfig.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay; + +import lombok.Data; + +/* + * 计全支付plus, 通用配置信息 + * + * @author yurong + * @site https://www.jeequan.com + * @date 2022/8/11 14:32 + */ +@Data +public class JeepluspayConfig { + + /** 签名类型 */ + public static final String DEFAULT_SIGN_TYPE = "MD5"; + public static final String SIGN_TYPE_RSA2 = "RSA2"; + + /** 支付订单状态 */ + public static String PAY_STATE_SUCCESS = "2"; // 2-支付成功 + public static String PAY_STATE_FAIL = "3"; // 3-支付失败 + + /** 退款订单状态 */ + public static String Refund_STATE_SUCCESS = "2"; // 2-退款成功 + public static String Refund_STATE_FAIL = "3"; // 3-退款失败 + + /** 支付方式 */ + public static String ALI_BAR = "ALI_BAR"; // 支付宝条码 + public static String ALI_JSAPI = "ALI_JSAPI"; // 支付宝生活号 + public static String ALI_APP = "ALI_APP"; // 支付宝APP + public static String ALI_WAP = "ALI_WAP"; // 支付宝WAP + public static String ALI_PC = "ALI_PC"; // 支付宝PC网站 + public static String ALI_QR = "ALI_QR"; // 支付宝二维码 + public static String WX_BAR = "WX_BAR"; // 微信条码 + public static String WX_JSAPI = "WX_JSAPI"; // 微信公众号 + public static String WX_LITE = "WX_LITE"; // 微信小程序 + public static String WX_APP = "WX_APP"; // 微信APP + public static String WX_H5 = "WX_H5"; // 微信H5 + public static String WX_NATIVE = "WX_NATIVE"; // 微信扫码 +} \ No newline at end of file diff --git a/jeepay-core/src/main/java/com/jeequan/jeepay/core/model/params/jeepluspay/JeepluspayNormalMchParams.java b/jeepay-core/src/main/java/com/jeequan/jeepay/core/model/params/jeepluspay/JeepluspayNormalMchParams.java new file mode 100644 index 0000000..e73ba95 --- /dev/null +++ b/jeepay-core/src/main/java/com/jeequan/jeepay/core/model/params/jeepluspay/JeepluspayNormalMchParams.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay; + +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; + +/* + * 计全支付plus, 普通商户参数定义 + * + * @author yurong + * @site https://www.jeequan.com + * @date 2022/8/11 14:32 + */ +@Data +public class JeepluspayNormalMchParams extends NormalMchParams { + + /** 商户号 */ + private String merchantNo; + + /** 应用ID */ + private String appId; + + /** 签名方式 **/ + private String signType; + + /** md5秘钥 */ + private String AppSecret; + + /** RSA2: 应用私钥 */ + private String rsa2AppPrivateKey; + + /** RSA2: 支付网关公钥 */ + public String rsa2PayPublicKey; + + + @Override + public String deSenData() { + + JeepluspayNormalMchParams mchParams = this; + if (StringUtils.isNotBlank(this.AppSecret)) { + mchParams.setAppSecret(StringKit.str2Star(this.AppSecret, 4, 4, 6)); + } + return ((JSONObject) JSON.toJSON(mchParams)).toJSONString(); + } + +} diff --git a/jeepay-payment/pom.xml b/jeepay-payment/pom.xml index 4fd63f0..b049c6b 100644 --- a/jeepay-payment/pom.xml +++ b/jeepay-payment/pom.xml @@ -15,6 +15,12 @@ jeepay Final + + + + ${basedir}/../ + + @@ -118,6 +124,15 @@ 1.2.2 + + + com.jeequan + jeepay-sdk-java + 1.0.0 + system + ${projectRootDir}/libs/jeepay-sdk-java-pls-1.0.0.jar + + diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayChannelNoticeService.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayChannelNoticeService.java new file mode 100644 index 0000000..d904608 --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayChannelNoticeService.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay; + +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.jeepluspay.JeepluspayConfig; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayNormalMchParams; +import com.jeequan.jeepay.pay.channel.AbstractChannelNoticeService; +import com.jeequan.jeepay.pay.model.MchAppConfigContext; +import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg; +import com.jeequan.jeepay.util.JeepayKit; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.MutablePair; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; + +/* + * 计全付 回调 + * + * @author yr + * @site https://www.jeequan.com + * @date 2022/7/20 10:31 + */ +@Service +@Slf4j +public class JeepluspayChannelNoticeService extends AbstractChannelNoticeService { + + @Override + public String getIfCode() { + return CS.IF_CODE.JEEPLUSPAY; + } + + @Override + public MutablePair 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 { + String logPrefix = "【处理计全付回调】"; + // 获取请求参数 + JSONObject paramsJson = (JSONObject) params; + log.info("{} 回调参数, jsonParams:{}", logPrefix, paramsJson); + // 校验签名 + String sign = paramsJson.getString("sign"); + boolean verifyResult = verifyParams(paramsJson, sign, mchAppConfigContext); + // 验证参数失败 + if (!verifyResult) { + throw ResponseException.buildText("ERROR"); + } + log.info("{}验证支付通知数据及签名通过", logPrefix); + // 验签成功后判断上游订单状态 + ResponseEntity okResponse = textResp("success"); + + // 支付状态: 0-订单生成, 1-支付中, 2-支付成功, 3-支付失败, 4-已撤销, 5-已退款, 6-订单关闭 + String status = paramsJson.getString("state"); + ChannelRetMsg result = new ChannelRetMsg(); + result.setResponseEntity(okResponse); + result.setChannelOrderId(paramsJson.getString("payOrderId")); + result.setChannelState(ChannelRetMsg.ChannelState.WAITING); // 默认支付中 + if (JeepluspayConfig.PAY_STATE_SUCCESS.equals(status)) { + result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS); + } else if (JeepluspayConfig.PAY_STATE_FAIL.equals(status)) { + result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + } + return result; + } catch (Exception e) { + log.error("error", e); + throw ResponseException.buildText("ERROR"); + } + } + + /** + * 验证计全付通知参数 + * + * @return boolean true or false + */ + public boolean verifyParams(JSONObject jsonParams, String sign, MchAppConfigContext mchAppConfigContext) { + try { + // 返回数据 + if (StringUtils.isEmpty(sign)) { + log.info("验签参数为空 [sign] :{}", sign); + return false; + } + JeepluspayNormalMchParams normalMchParams = (JeepluspayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.JEEPLUSPAY); + jsonParams.remove("sign"); + // 获取md5秘钥,生成签名 + String newSign = JeepayKit.getSign(jsonParams, normalMchParams.getAppSecret()); + // 验签 异步时都是MD5 + if (!sign.equals(newSign)) { + log.info("验签失败! 回调参数:parameter = {}", jsonParams); + return false; + } + return true; + } catch (Exception e) { + log.error("error", e); + throw ResponseException.buildText("ERROR"); + } + } +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayChannelRefundNoticeService.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayChannelRefundNoticeService.java new file mode 100644 index 0000000..44d2c2c --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayChannelRefundNoticeService.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay; + +import com.alibaba.fastjson.JSONObject; +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.core.entity.RefundOrder; +import com.jeequan.jeepay.core.exception.ResponseException; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayConfig; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayNormalMchParams; +import com.jeequan.jeepay.pay.channel.AbstractChannelRefundNoticeService; +import com.jeequan.jeepay.pay.model.MchAppConfigContext; +import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg; +import com.jeequan.jeepay.util.JeepayKit; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.MutablePair; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; + +/* + * 计全支付plus 退款回调接口实现类 + * + * @author yr + * @site https://www.jeequan.com + * @date 2022/7/20 10:31 + */ +@Service +@Slf4j +public class JeepluspayChannelRefundNoticeService extends AbstractChannelRefundNoticeService { + + @Override + public String getIfCode() { + return CS.IF_CODE.JEEPLUSPAY; + } + + @Override + public MutablePair parseParams(HttpServletRequest request, String urlOrderId, NoticeTypeEnum noticeTypeEnum) { + + try { + JSONObject params = getReqParamJSON(); + String refundOrderId = params.getString("mchRefundNo"); + return MutablePair.of(refundOrderId, params); + + } catch (Exception e) { + log.error("error", e); + throw ResponseException.buildText("ERROR"); + } + } + + @Override + public ChannelRetMsg doNotice(HttpServletRequest request, Object params, RefundOrder refundOrder, MchAppConfigContext mchAppConfigContext, NoticeTypeEnum noticeTypeEnum) { + try { + String logPrefix = "【处理计全付退款回调】"; + // 获取请求参数 + JSONObject jsonParams = (JSONObject) params; + log.info("{} 回调参数, jsonParams:{}", logPrefix, jsonParams); + // 校验签名 + String sign = jsonParams.getString("sign"); + boolean verifyResult = verifyParams(jsonParams, sign, mchAppConfigContext); + // 验证参数失败 + if (!verifyResult) { + throw ResponseException.buildText("ERROR"); + } + log.info("{}验证退款通知数据及签名通过", logPrefix); + //验签成功后判断上游订单状态 + ResponseEntity okResponse = textResp("success"); + + // 退款状态 0-订单生成 1-退款中 2-退款成功 3-退款失败 4-退款关闭 + String status = jsonParams.getString("state"); + + ChannelRetMsg result = new ChannelRetMsg(); + result.setChannelOrderId(jsonParams.getString("refundOrderId")); //渠道订单号 + result.setResponseEntity(okResponse); //响应数据 + + result.setChannelState(ChannelRetMsg.ChannelState.WAITING); // 默认退款中 + + if (JeepluspayConfig.Refund_STATE_SUCCESS.equals(status)) { + result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS); + }else if (JeepluspayConfig.Refund_STATE_FAIL.equals(status)) { + result.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + } + return result; + } catch (Exception e) { + log.error("error", e); + throw ResponseException.buildText("ERROR"); + } + } + + /** + * 验证计全支付plus通知参数 + * + * @return boolean true or false + */ + public boolean verifyParams(JSONObject jsonParams, String sign, MchAppConfigContext mchAppConfigContext) { + try { + // 返回数据 + if (StringUtils.isEmpty(sign)) { + log.info("验签参数为空 [sign] :{}", sign); + return false; + } + JeepluspayNormalMchParams normalMchParams = (JeepluspayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.JEEPLUSPAY); + jsonParams.remove("sign"); + // 获取md5秘钥,生成签名 + String newSign = JeepayKit.getSign(jsonParams, normalMchParams.getAppSecret()); + // 验签 异步时都是MD5 + if (!sign.equals(newSign)) { + log.info("验签失败! 回调参数:parameter = {}", jsonParams); + return false; + } + return true; + } catch (Exception e) { + log.error("error", e); + throw ResponseException.buildText("ERROR"); + } + } + +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayPayOrderQueryService.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayPayOrderQueryService.java new file mode 100644 index 0000000..bbf568e --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayPayOrderQueryService.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay; + +import com.jeequan.jeepay.Jeepay; +import com.jeequan.jeepay.JeepayClient; +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.core.entity.PayOrder; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayConfig; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayNormalMchParams; +import com.jeequan.jeepay.model.PayOrderQueryReqModel; +import com.jeequan.jeepay.pay.channel.IPayOrderQueryService; +import com.jeequan.jeepay.pay.model.MchAppConfigContext; +import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg; +import com.jeequan.jeepay.pay.service.ConfigContextQueryService; +import com.jeequan.jeepay.request.PayOrderQueryRequest; +import com.jeequan.jeepay.response.PayOrderQueryResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 计全支付plus 查询订单 + * + * @author yurong + * @site https://www.jeequan.com + * @date 2022/7/20 9:31 + */ +@Service +@Slf4j +public class JeepluspayPayOrderQueryService implements IPayOrderQueryService { + @Autowired + private ConfigContextQueryService configContextQueryService; + + @Override + public String getIfCode() { + return CS.IF_CODE.JEEPLUSPAY; + } + + @Override + public ChannelRetMsg query(PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception { + PayOrderQueryRequest request = new PayOrderQueryRequest(); + PayOrderQueryReqModel model = new PayOrderQueryReqModel(); + try { + JeepluspayNormalMchParams normalMchParams = (JeepluspayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.JEEPLUSPAY); + model.setMchNo(normalMchParams.getMerchantNo()); // 商户号 + model.setAppId(normalMchParams.getAppId()); // 应用ID + model.setMchOrderNo(payOrder.getPayOrderId()); // 支付订单号 + request.setBizModel(model); + // 发起请求 + PayOrderQueryResponse response = new PayOrderQueryResponse(); + boolean checkSign = false; + boolean isSuccess = false; + if (normalMchParams.getSignType().equals(JeepluspayConfig.DEFAULT_SIGN_TYPE) || StringUtils.isEmpty(normalMchParams.getSignType())) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getAppSecret(), Jeepay.getApiBase()); + response = jeepayClient.execute(request); + checkSign = response.checkSign(normalMchParams.getAppSecret()); + isSuccess = response.isSuccess(normalMchParams.getAppSecret()); + + } else if (normalMchParams.getSignType().equals(JeepluspayConfig.SIGN_TYPE_RSA2)) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getRsa2AppPrivateKey(), Jeepay.getApiBase()); + response = jeepayClient.executeByRSA2(request); + checkSign = response.checkSignByRsa2(normalMchParams.getRsa2PayPublicKey()); + isSuccess = response.isSuccessByRsa2(normalMchParams.getRsa2PayPublicKey()); + } + + // 请求响应状态 + if (isSuccess && checkSign) { + // 如果查询请求成功 + if (JeepluspayConfig.PAY_STATE_SUCCESS.equals(String.valueOf(response.get().getState()))) { + return ChannelRetMsg.confirmSuccess(response.get().getPayOrderId()); + } else if (JeepluspayConfig.PAY_STATE_FAIL.equals(String.valueOf(response.get().getState()))) { + // 失败 + return ChannelRetMsg.confirmFail(); + } + } + // 支付中 + return ChannelRetMsg.waiting(); + } catch (Exception e) { + // 支付中 + return ChannelRetMsg.waiting(); + } + } +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayPaymentService.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayPaymentService.java new file mode 100644 index 0000000..1ee3940 --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayPaymentService.java @@ -0,0 +1,41 @@ +package com.jeequan.jeepay.pay.channel.jeepluspay; + +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; + +/** + * 计全支付plus + * + * @author yurong + * @site https://www.jeequan.com + * @date 2022/8/11 15:37 + */ +@Service +public class JeepluspayPaymentService extends AbstractPaymentService { + + @Override + public String getIfCode() { + return CS.IF_CODE.JEEPLUSPAY; + } + + @Override + public boolean isSupport(String wayCode) { + return true; + } + + @Override + public String preCheck(UnifiedOrderRQ bizRQ, PayOrder payOrder) { + return PaywayUtil.getRealPaywayService(this, payOrder.getWayCode()).preCheck(bizRQ, payOrder); + } + + @Override + public AbstractRS pay(UnifiedOrderRQ bizRQ, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception { + return PaywayUtil.getRealPaywayService(this, payOrder.getWayCode()).pay(bizRQ, payOrder, mchAppConfigContext); + } +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayRefundService.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayRefundService.java new file mode 100644 index 0000000..1ca4961 --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/JeepluspayRefundService.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay; + +import com.jeequan.jeepay.Jeepay; +import com.jeequan.jeepay.JeepayClient; +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.core.entity.PayOrder; +import com.jeequan.jeepay.core.entity.RefundOrder; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayConfig; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayNormalMchParams; +import com.jeequan.jeepay.exception.JeepayException; +import com.jeequan.jeepay.model.RefundOrderCreateReqModel; +import com.jeequan.jeepay.model.RefundOrderQueryReqModel; +import com.jeequan.jeepay.pay.channel.AbstractRefundService; +import com.jeequan.jeepay.pay.model.MchAppConfigContext; +import com.jeequan.jeepay.pay.rqrs.msg.ChannelRetMsg; +import com.jeequan.jeepay.pay.rqrs.refund.RefundOrderRQ; +import com.jeequan.jeepay.request.RefundOrderCreateRequest; +import com.jeequan.jeepay.request.RefundOrderQueryRequest; +import com.jeequan.jeepay.response.RefundOrderCreateResponse; +import com.jeequan.jeepay.response.RefundOrderQueryResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +/** + * 退款接口: 计全退款plus + * + * @author yurong + * @site https://www.jeequan.com + * @date 2022/8/16 15:28 + */ +@Service +public class JeepluspayRefundService extends AbstractRefundService { + + @Override + public String getIfCode() { + return CS.IF_CODE.JEEPLUSPAY; + } + + @Override + public String preCheck(RefundOrderRQ bizRQ, RefundOrder refundOrder, PayOrder payOrder) { + return null; + } + + @Override + public ChannelRetMsg refund(RefundOrderRQ bizRQ, RefundOrder refundOrder, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception { + JeepluspayNormalMchParams normalMchParams = (JeepluspayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.JEEPLUSPAY); + RefundOrderCreateRequest request = new RefundOrderCreateRequest(); + RefundOrderCreateReqModel model = new RefundOrderCreateReqModel(); + model.setMchNo(normalMchParams.getMerchantNo()); // 商户号 + model.setAppId(normalMchParams.getAppId()); // 应用ID + model.setPayOrderId(payOrder.getChannelOrderNo()); // 支付订单号 + model.setMchRefundNo(refundOrder.getRefundOrderId()); // 商户退款单号 + model.setRefundAmount(refundOrder.getRefundAmount()); // 金额,单位分 + model.setCurrency(refundOrder.getCurrency()); // 币种,目前只支持cny + model.setRefundReason(refundOrder.getRefundReason()); // 退款原因 + model.setClientIp(refundOrder.getClientIp()); // 发起退款请求客户端的IP地址 + model.setNotifyUrl(getNotifyUrl()); // 异步通知地址 + request.setBizModel(model); + // 构造函数响应数据 + ChannelRetMsg channelRetMsg = new ChannelRetMsg(); + try { + // 发起退款 + RefundOrderCreateResponse response = new RefundOrderCreateResponse(); + boolean checkSign = false; + boolean isSuccess = false; + if (normalMchParams.getSignType().equals(JeepluspayConfig.DEFAULT_SIGN_TYPE) || StringUtils.isEmpty(normalMchParams.getSignType())) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getAppSecret(), Jeepay.getApiBase()); + response = jeepayClient.execute(request); + checkSign = response.checkSign(normalMchParams.getAppSecret()); + isSuccess = response.isSuccess(normalMchParams.getAppSecret()); + + } else if (normalMchParams.getSignType().equals(JeepluspayConfig.SIGN_TYPE_RSA2)) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getRsa2AppPrivateKey(), Jeepay.getApiBase()); + response = jeepayClient.executeByRSA2(request); + checkSign = response.checkSignByRsa2(normalMchParams.getRsa2PayPublicKey()); + isSuccess = response.isSuccessByRsa2(normalMchParams.getRsa2PayPublicKey()); + } + + if (checkSign) { + channelRetMsg.setChannelOrderId(response.get().getRefundOrderId()); + // 退款发送成功 + if (isSuccess) { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS); + } else { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + channelRetMsg.setChannelErrCode(response.getCode().toString()); + channelRetMsg.setChannelErrMsg(response.getMsg()); + } + } + } catch (JeepayException e) { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + } + return channelRetMsg; + } + + @Override + public ChannelRetMsg query(RefundOrder refundOrder, MchAppConfigContext mchAppConfigContext) throws Exception { + RefundOrderQueryRequest request = new RefundOrderQueryRequest(); + RefundOrderQueryReqModel model = new RefundOrderQueryReqModel(); + try { + JeepluspayNormalMchParams normalMchParams = (JeepluspayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.JEEPLUSPAY); + model.setMchNo(normalMchParams.getMerchantNo()); // 商户号 + model.setAppId(normalMchParams.getAppId()); // 应用ID + model.setRefundOrderId(refundOrder.getRefundOrderId()); // 退款订单号 + request.setBizModel(model); + // 发起请求 + RefundOrderQueryResponse response = new RefundOrderQueryResponse(); + boolean checkSign = false; + boolean isSuccess = false; + if (normalMchParams.getSignType().equals(JeepluspayConfig.DEFAULT_SIGN_TYPE) || StringUtils.isEmpty(normalMchParams.getSignType())) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getAppSecret(), Jeepay.getApiBase()); + response = jeepayClient.execute(request); + checkSign = response.checkSign(normalMchParams.getAppSecret()); + isSuccess = response.isSuccessByRsa2(normalMchParams.getRsa2PayPublicKey()); + } else if (normalMchParams.getSignType().equals(JeepluspayConfig.SIGN_TYPE_RSA2)) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getRsa2AppPrivateKey(), Jeepay.getApiBase()); + response = jeepayClient.executeByRSA2(request); + checkSign = response.checkSignByRsa2(normalMchParams.getRsa2PayPublicKey()); + isSuccess = response.isSuccessByRsa2(normalMchParams.getRsa2PayPublicKey()); + } + + // 请求响应状态 + if (isSuccess && checkSign) { + // 如果查询请求成功 + if (JeepluspayConfig.PAY_STATE_SUCCESS.equals(response.get().getState().toString())) { + return ChannelRetMsg.confirmSuccess(response.get().getRefundOrderId()); + } else if (JeepluspayConfig.PAY_STATE_FAIL.equals(response.get().getState().toString())) { + // 失败 + return ChannelRetMsg.confirmFail(); + } + } + // 退款中 + return ChannelRetMsg.waiting(); + } catch (Exception e) { + // 退款中 + return ChannelRetMsg.waiting(); + } + } +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliApp.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliApp.java new file mode 100644 index 0000000..6c4432b --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliApp.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay.payway; + +import com.alibaba.fastjson.JSONObject; +import com.jeequan.jeepay.Jeepay; +import com.jeequan.jeepay.JeepayClient; +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.core.entity.PayOrder; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayConfig; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayNormalMchParams; +import com.jeequan.jeepay.exception.JeepayException; +import com.jeequan.jeepay.model.PayOrderCreateReqModel; +import com.jeequan.jeepay.pay.channel.jeepluspay.JeepluspayPaymentService; +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.AliAppOrderRS; +import com.jeequan.jeepay.pay.util.ApiResBuilder; +import com.jeequan.jeepay.request.PayOrderCreateRequest; +import com.jeequan.jeepay.response.PayOrderCreateResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +/* + * 计全付 支付宝 APP支付 + * + * @author yr + * @site https://www.jeequan.com + * @date 2022/8/6 16:20 + */ +@Service("jeepluspayPaymentByAliAppService") //Service Name需保持全局唯一性 +public class AliApp extends JeepluspayPaymentService { + + @Override + public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) { + return null; + } + + @Override + public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) { + JeepluspayNormalMchParams normalMchParams = (JeepluspayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.JEEPLUSPAY); + // 构建请求数据 + PayOrderCreateRequest request = new PayOrderCreateRequest(); + PayOrderCreateReqModel model = new PayOrderCreateReqModel(); + model.setMchNo(normalMchParams.getMerchantNo()); // 商户号 + model.setAppId(normalMchParams.getAppId()); // 应用ID + model.setMchOrderNo(payOrder.getPayOrderId()); // 商户订单号 + model.setWayCode(JeepluspayConfig.ALI_APP); // 支付方式 + model.setAmount(payOrder.getAmount()); // 金额,单位分 + model.setCurrency(payOrder.getCurrency()); // 币种,目前只支持cny + model.setClientIp(payOrder.getClientIp()); // 发起支付请求客户端的IP地址 + model.setSubject(payOrder.getSubject()); // 商品标题 + model.setBody(payOrder.getBody()); // 商品描述 + model.setNotifyUrl(getNotifyUrl()); // 异步通知地址 + JSONObject channelExtra = new JSONObject(); + channelExtra.put("payDataType", CS.PAY_DATA_TYPE.ALI_APP); + model.setChannelExtra(channelExtra.toString()); // 支付宝app支付参数 + request.setBizModel(model); + // 构造函数响应数据 + AliAppOrderRS res = ApiResBuilder.buildSuccess(AliAppOrderRS.class); + ChannelRetMsg channelRetMsg = new ChannelRetMsg(); + res.setChannelRetMsg(channelRetMsg); + try { + // 发起统一下单 + PayOrderCreateResponse response = new PayOrderCreateResponse(); + boolean checkSign = false; + boolean isSuccess = false; + if (normalMchParams.getSignType().equals(JeepluspayConfig.DEFAULT_SIGN_TYPE) || StringUtils.isEmpty(normalMchParams.getSignType())) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getAppSecret(), Jeepay.getApiBase()); + response = jeepayClient.execute(request); + checkSign = response.checkSign(normalMchParams.getAppSecret()); + isSuccess = response.isSuccess(normalMchParams.getAppSecret()); + + } else if (normalMchParams.getSignType().equals(JeepluspayConfig.SIGN_TYPE_RSA2)) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getRsa2AppPrivateKey(), Jeepay.getApiBase()); + response = jeepayClient.executeByRSA2(request); + checkSign = response.checkSignByRsa2(normalMchParams.getRsa2PayPublicKey()); + isSuccess = response.isSuccessByRsa2(normalMchParams.getRsa2PayPublicKey()); + } + + if (checkSign) { + channelRetMsg.setChannelOrderId(response.get().getPayOrderId()); + if (isSuccess) { + // 下单成功 + JSONObject payData = response.getData().getJSONObject("payData"); + res.setPayData(payData.toJSONString()); + channelRetMsg.setChannelAttach(payData.toJSONString()); + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING); + } else { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + channelRetMsg.setChannelErrCode(response.get().getErrCode()); + channelRetMsg.setChannelErrMsg(response.get().getErrMsg()); + } + } + } catch (JeepayException e) { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + } + return res; + } +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliBar.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliBar.java new file mode 100644 index 0000000..3db8c63 --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliBar.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay.payway; + +import com.alibaba.fastjson.JSONObject; +import com.jeequan.jeepay.Jeepay; +import com.jeequan.jeepay.JeepayClient; +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.core.entity.PayOrder; +import com.jeequan.jeepay.core.exception.BizException; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayConfig; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayNormalMchParams; +import com.jeequan.jeepay.exception.JeepayException; +import com.jeequan.jeepay.model.PayOrderCreateReqModel; +import com.jeequan.jeepay.pay.channel.jeepluspay.JeepluspayPaymentService; +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 com.jeequan.jeepay.request.PayOrderCreateRequest; +import com.jeequan.jeepay.response.PayOrderCreateResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +/* + * 计全付 支付宝 条码支付 + * + * @author yr + * @site https://www.jeequan.com + * @date 2022/8/11 15:37 + */ +@Service("jeepluspayPaymentByAliBarService") //Service Name需保持全局唯一性 +public class AliBar extends JeepluspayPaymentService { + + @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; + JeepluspayNormalMchParams normalMchParams = (JeepluspayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.JEEPLUSPAY); + // 构建请求数据 + PayOrderCreateRequest request = new PayOrderCreateRequest(); + PayOrderCreateReqModel model = new PayOrderCreateReqModel(); + model.setMchNo(normalMchParams.getMerchantNo()); // 商户号 + model.setAppId(normalMchParams.getAppId()); // 应用ID + model.setMchOrderNo(payOrder.getPayOrderId()); // 商户订单号 + model.setWayCode(JeepluspayConfig.ALI_BAR); // 支付方式 + model.setAmount(payOrder.getAmount()); // 金额,单位分 + model.setCurrency(payOrder.getCurrency()); // 币种,目前只支持cny + model.setClientIp(payOrder.getClientIp()); // 发起支付请求客户端的IP地址 + model.setSubject(payOrder.getSubject()); // 商品标题 + model.setBody(payOrder.getBody()); // 商品描述 + model.setNotifyUrl(getNotifyUrl()); // 异步通知地址 + JSONObject channelExtra = new JSONObject(); + channelExtra.put("authCode", bizRQ.getAuthCode()); + model.setChannelExtra(channelExtra.toString()); // 用户付款码值 + request.setBizModel(model); + // 构造函数响应数据 + AliBarOrderRS res = ApiResBuilder.buildSuccess(AliBarOrderRS.class); + ChannelRetMsg channelRetMsg = new ChannelRetMsg(); + res.setChannelRetMsg(channelRetMsg); + try { + // 发起统一下单 + PayOrderCreateResponse response = new PayOrderCreateResponse(); + boolean checkSign = false; + boolean isSuccess = false; + if (normalMchParams.getSignType().equals(JeepluspayConfig.DEFAULT_SIGN_TYPE) || StringUtils.isEmpty(normalMchParams.getSignType())) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getAppSecret(), Jeepay.getApiBase()); + response = jeepayClient.execute(request); + checkSign = response.checkSign(normalMchParams.getAppSecret()); + isSuccess = response.isSuccess(normalMchParams.getAppSecret()); + + } else if (normalMchParams.getSignType().equals(JeepluspayConfig.SIGN_TYPE_RSA2)) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getRsa2AppPrivateKey(), Jeepay.getApiBase()); + response = jeepayClient.executeByRSA2(request); + checkSign = response.checkSignByRsa2(normalMchParams.getRsa2PayPublicKey()); + isSuccess = response.isSuccessByRsa2(normalMchParams.getRsa2PayPublicKey()); + } + + if (checkSign) { + channelRetMsg.setChannelOrderId(response.get().getPayOrderId()); + if (isSuccess) { + // 下单成功 + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS); + } else { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + channelRetMsg.setChannelErrCode(response.get().getErrCode()); + channelRetMsg.setChannelErrMsg(response.get().getErrMsg()); + } + } + } catch (JeepayException e) { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + } + return res; + } +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliJsapi.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliJsapi.java new file mode 100644 index 0000000..edb18c8 --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliJsapi.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay.payway; + +import com.alibaba.fastjson.JSONObject; +import com.jeequan.jeepay.Jeepay; +import com.jeequan.jeepay.JeepayClient; +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.core.entity.PayOrder; +import com.jeequan.jeepay.core.exception.BizException; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayConfig; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayNormalMchParams; +import com.jeequan.jeepay.exception.JeepayException; +import com.jeequan.jeepay.model.PayOrderCreateReqModel; +import com.jeequan.jeepay.pay.channel.jeepluspay.JeepluspayPaymentService; +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.AliJsapiOrderRQ; +import com.jeequan.jeepay.pay.rqrs.payorder.payway.AliJsapiOrderRS; +import com.jeequan.jeepay.pay.util.ApiResBuilder; +import com.jeequan.jeepay.request.PayOrderCreateRequest; +import com.jeequan.jeepay.response.PayOrderCreateResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +/* + * 计全付 支付宝 jsapi支付 + * + * @author yr + * @site https://www.jeequan.com + * @date 2022/8/15 09:46 + */ +@Service("jeepluspayPaymentByAliJsapiService") //Service Name需保持全局唯一性 +public class AliJsapi extends JeepluspayPaymentService { + + @Override + public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) { + AliJsapiOrderRQ bizRQ = (AliJsapiOrderRQ) rq; + if (StringUtils.isEmpty(bizRQ.getBuyerUserId())) { + throw new BizException("[buyerUserId]不可为空"); + } + return null; + } + + @Override + public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception { + AliJsapiOrderRQ bizRQ = (AliJsapiOrderRQ) rq; + JeepluspayNormalMchParams normalMchParams = (JeepluspayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.JEEPLUSPAY); + // 构建请求数据 + PayOrderCreateRequest request = new PayOrderCreateRequest(); + PayOrderCreateReqModel model = new PayOrderCreateReqModel(); + model.setMchNo(normalMchParams.getMerchantNo()); // 商户号 + model.setAppId(normalMchParams.getAppId()); // 应用ID + model.setMchOrderNo(payOrder.getPayOrderId()); // 商户订单号 + model.setWayCode(JeepluspayConfig.ALI_JSAPI); // 支付方式 + model.setAmount(payOrder.getAmount()); // 金额,单位分 + model.setCurrency(payOrder.getCurrency()); // 币种,目前只支持cny + model.setClientIp(payOrder.getClientIp()); // 发起支付请求客户端的IP地址 + model.setSubject(payOrder.getSubject()); // 商品标题 + model.setBody(payOrder.getBody()); // 商品描述 + model.setNotifyUrl(getNotifyUrl()); // 异步通知地址 + JSONObject channelExtra = new JSONObject(); + channelExtra.put("buyerUserId", bizRQ.getBuyerUserId()); + model.setChannelExtra(channelExtra.toString()); // 支付宝用户ID + request.setBizModel(model); + // 构造函数响应数据 + AliJsapiOrderRS res = ApiResBuilder.buildSuccess(AliJsapiOrderRS.class); + ChannelRetMsg channelRetMsg = new ChannelRetMsg(); + res.setChannelRetMsg(channelRetMsg); + try { + // 发起统一下单 + PayOrderCreateResponse response = new PayOrderCreateResponse(); + boolean checkSign = false; + boolean isSuccess = false; + if (normalMchParams.getSignType().equals(JeepluspayConfig.DEFAULT_SIGN_TYPE) || StringUtils.isEmpty(normalMchParams.getSignType())) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getAppSecret(), Jeepay.getApiBase()); + response = jeepayClient.execute(request); + checkSign = response.checkSign(normalMchParams.getAppSecret()); + isSuccess = response.isSuccess(normalMchParams.getAppSecret()); + + } else if (normalMchParams.getSignType().equals(JeepluspayConfig.SIGN_TYPE_RSA2)) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getRsa2AppPrivateKey(), Jeepay.getApiBase()); + response = jeepayClient.executeByRSA2(request); + checkSign = response.checkSignByRsa2(normalMchParams.getRsa2PayPublicKey()); + isSuccess = response.isSuccessByRsa2(normalMchParams.getRsa2PayPublicKey()); + } + + if (checkSign) { + channelRetMsg.setChannelOrderId(response.get().getPayOrderId()); + if (isSuccess) { + // 下单成功 + JSONObject payData = response.getData().getJSONObject("payData"); + res.setAlipayTradeNo(payData.getString("alipayTradeNo")); + res.setPayData(payData.toJSONString()); + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING); + } else { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + channelRetMsg.setChannelErrCode(response.get().getErrCode()); + channelRetMsg.setChannelErrMsg(response.get().getErrMsg()); + } + } + } catch (JeepayException e) { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + } + return res; + } +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliLite.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliLite.java new file mode 100644 index 0000000..e3123d3 --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliLite.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay.payway; + +import com.alibaba.fastjson.JSONObject; +import com.jeequan.jeepay.Jeepay; +import com.jeequan.jeepay.JeepayClient; +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.core.entity.PayOrder; +import com.jeequan.jeepay.core.exception.BizException; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayConfig; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayNormalMchParams; +import com.jeequan.jeepay.exception.JeepayException; +import com.jeequan.jeepay.model.PayOrderCreateReqModel; +import com.jeequan.jeepay.pay.channel.jeepluspay.JeepluspayPaymentService; +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.AliLiteOrderRQ; +import com.jeequan.jeepay.pay.rqrs.payorder.payway.AliLiteOrderRS; +import com.jeequan.jeepay.pay.util.ApiResBuilder; +import com.jeequan.jeepay.request.PayOrderCreateRequest; +import com.jeequan.jeepay.response.PayOrderCreateResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +/* + * 计全付 支付宝 小程序支付 + * + * @author yr + * @site https://www.jeequan.com + * @date 2022/8/17 17:24 + */ +@Service("jeepluspayPaymentByAliLiteService") //Service Name需保持全局唯一性 +public class AliLite extends JeepluspayPaymentService { + + @Override + public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) { + AliLiteOrderRQ bizRQ = (AliLiteOrderRQ) rq; + if (StringUtils.isEmpty(bizRQ.getBuyerUserId())) { + throw new BizException("[buyerUserId]不可为空"); + } + return null; + } + + @Override + public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception { + AliLiteOrderRQ bizRQ = (AliLiteOrderRQ) rq; + JeepluspayNormalMchParams normalMchParams = (JeepluspayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.JEEPLUSPAY); + // 构建请求数据 + PayOrderCreateRequest request = new PayOrderCreateRequest(); + PayOrderCreateReqModel model = new PayOrderCreateReqModel(); + model.setMchNo(normalMchParams.getMerchantNo()); // 商户号 + model.setAppId(normalMchParams.getAppId()); // 应用ID + model.setMchOrderNo(payOrder.getPayOrderId()); // 商户订单号 + model.setWayCode(JeepluspayConfig.WX_LITE); // 支付方式 + model.setAmount(payOrder.getAmount()); // 金额,单位分 + model.setCurrency(payOrder.getCurrency()); // 币种,目前只支持cny + model.setClientIp(payOrder.getClientIp()); // 发起支付请求客户端的IP地址 + model.setSubject(payOrder.getSubject()); // 商品标题 + model.setBody(payOrder.getBody()); // 商品描述 + model.setNotifyUrl(getNotifyUrl()); // 异步通知地址 + JSONObject channelExtra = new JSONObject(); + channelExtra.put("buyerUserId", bizRQ.getBuyerUserId()); + model.setChannelExtra(channelExtra.toString()); // 支付宝用户ID + request.setBizModel(model); + // 构造函数响应数据 + AliLiteOrderRS res = ApiResBuilder.buildSuccess(AliLiteOrderRS.class); + ChannelRetMsg channelRetMsg = new ChannelRetMsg(); + res.setChannelRetMsg(channelRetMsg); + try { + // 发起统一下单 + PayOrderCreateResponse response = new PayOrderCreateResponse(); + boolean checkSign = false; + boolean isSuccess = false; + if (normalMchParams.getSignType().equals(JeepluspayConfig.DEFAULT_SIGN_TYPE) || StringUtils.isEmpty(normalMchParams.getSignType())) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getAppSecret(), Jeepay.getApiBase()); + response = jeepayClient.execute(request); + checkSign = response.checkSign(normalMchParams.getAppSecret()); + isSuccess = response.isSuccess(normalMchParams.getAppSecret()); + + } else if (normalMchParams.getSignType().equals(JeepluspayConfig.SIGN_TYPE_RSA2)) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getRsa2AppPrivateKey(), Jeepay.getApiBase()); + response = jeepayClient.executeByRSA2(request); + checkSign = response.checkSignByRsa2(normalMchParams.getRsa2PayPublicKey()); + isSuccess = response.isSuccessByRsa2(normalMchParams.getRsa2PayPublicKey()); + } + + if (checkSign) { + channelRetMsg.setChannelOrderId(response.get().getPayOrderId()); + if (isSuccess) { + // 下单成功 + JSONObject payData = response.getData().getJSONObject("payData"); + res.setAlipayTradeNo(payData.getString("alipayTradeNo")); + res.setPayData(payData.toJSONString()); + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING); + } else { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + channelRetMsg.setChannelErrCode(response.get().getErrCode()); + channelRetMsg.setChannelErrMsg(response.get().getErrMsg()); + } + } + } catch (JeepayException e) { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + } + return res; + } +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliPc.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliPc.java new file mode 100644 index 0000000..3c9d92c --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliPc.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay.payway; + +import com.jeequan.jeepay.Jeepay; +import com.jeequan.jeepay.JeepayClient; +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.core.entity.PayOrder; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayConfig; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayNormalMchParams; +import com.jeequan.jeepay.exception.JeepayException; +import com.jeequan.jeepay.model.PayOrderCreateReqModel; +import com.jeequan.jeepay.pay.channel.jeepluspay.JeepluspayPaymentService; +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.AliPcOrderRS; +import com.jeequan.jeepay.pay.util.ApiResBuilder; +import com.jeequan.jeepay.request.PayOrderCreateRequest; +import com.jeequan.jeepay.response.PayOrderCreateResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +/* + * 计全付 支付宝 PC支付 + * + * @author yr + * @site https://www.jeequan.com + * @date 2022/8/17 14:51 + */ +@Service("jeepluspayPaymentByAliPcService") //Service Name需保持全局唯一性 +public class AliPc extends JeepluspayPaymentService { + + @Override + public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) { + return null; + } + + @Override + public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) { + JeepluspayNormalMchParams normalMchParams = (JeepluspayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.JEEPLUSPAY); + // 构建请求数据 + PayOrderCreateRequest request = new PayOrderCreateRequest(); + PayOrderCreateReqModel model = new PayOrderCreateReqModel(); + model.setMchNo(normalMchParams.getMerchantNo()); // 商户号 + model.setAppId(normalMchParams.getAppId()); // 应用ID + model.setMchOrderNo(payOrder.getPayOrderId()); // 商户订单号 + model.setWayCode(JeepluspayConfig.ALI_PC); // 支付方式 + model.setAmount(payOrder.getAmount()); // 金额,单位分 + model.setCurrency(payOrder.getCurrency()); // 币种,目前只支持cny + model.setClientIp(payOrder.getClientIp()); // 发起支付请求客户端的IP地址 + model.setSubject(payOrder.getSubject()); // 商品标题 + model.setBody(payOrder.getBody()); // 商品描述 + model.setNotifyUrl(getNotifyUrl()); // 异步通知地址 + request.setBizModel(model); + // 构造函数响应数据 + AliPcOrderRS res = ApiResBuilder.buildSuccess(AliPcOrderRS.class); + ChannelRetMsg channelRetMsg = new ChannelRetMsg(); + res.setChannelRetMsg(channelRetMsg); + try { + // 发起统一下单 + PayOrderCreateResponse response = new PayOrderCreateResponse(); + boolean checkSign = false; + boolean isSuccess = false; + if (normalMchParams.getSignType().equals(JeepluspayConfig.DEFAULT_SIGN_TYPE) || StringUtils.isEmpty(normalMchParams.getSignType())) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getAppSecret(), Jeepay.getApiBase()); + response = jeepayClient.execute(request); + checkSign = response.checkSign(normalMchParams.getAppSecret()); + isSuccess = response.isSuccess(normalMchParams.getAppSecret()); + + } else if (normalMchParams.getSignType().equals(JeepluspayConfig.SIGN_TYPE_RSA2)) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getRsa2AppPrivateKey(), Jeepay.getApiBase()); + response = jeepayClient.executeByRSA2(request); + checkSign = response.checkSignByRsa2(normalMchParams.getRsa2PayPublicKey()); + isSuccess = response.isSuccessByRsa2(normalMchParams.getRsa2PayPublicKey()); + } + + if (checkSign) { + channelRetMsg.setChannelOrderId(response.get().getPayOrderId()); + if (isSuccess) { + // 下单成功 + String payUrl = response.getData().getString("payData"); + String payDataType = response.getData().getString("payDataType"); + if (CS.PAY_DATA_TYPE.FORM.equals(payDataType)) { //表单方式 + res.setFormContent(payUrl); + } else if (CS.PAY_DATA_TYPE.CODE_IMG_URL.equals(payDataType)) { //二维码图片地址 + res.setCodeImgUrl(payUrl); + } else { // 默认都为 payUrl方式 + res.setPayUrl(payUrl); + } + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING); + } else { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + channelRetMsg.setChannelErrCode(response.get().getErrCode()); + channelRetMsg.setChannelErrMsg(response.get().getErrMsg()); + } + } + } catch (JeepayException e) { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + } + return res; + } +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliQr.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliQr.java new file mode 100644 index 0000000..7680fa3 --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliQr.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay.payway; + +import com.jeequan.jeepay.Jeepay; +import com.jeequan.jeepay.JeepayClient; +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.core.entity.PayOrder; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayConfig; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayNormalMchParams; +import com.jeequan.jeepay.exception.JeepayException; +import com.jeequan.jeepay.model.PayOrderCreateReqModel; +import com.jeequan.jeepay.pay.channel.jeepluspay.JeepluspayPaymentService; +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.AliQrOrderRQ; +import com.jeequan.jeepay.pay.rqrs.payorder.payway.AliQrOrderRS; +import com.jeequan.jeepay.pay.util.ApiResBuilder; +import com.jeequan.jeepay.request.PayOrderCreateRequest; +import com.jeequan.jeepay.response.PayOrderCreateResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +/* + * 计全付 支付宝 二维码支付 + * + * @author yr + * @site https://www.jeequan.com + * @date 2022/8/15 09:46 + */ +@Service("jeepluspayPaymentByAliQrService") //Service Name需保持全局唯一性 +public class AliQr extends JeepluspayPaymentService { + + @Override + public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) { + return null; + } + + @Override + public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception { + AliQrOrderRQ aliQrOrderRQ = (AliQrOrderRQ) rq; + JeepluspayNormalMchParams normalMchParams = (JeepluspayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.JEEPLUSPAY); + // 构建请求数据 + PayOrderCreateRequest request = new PayOrderCreateRequest(); + PayOrderCreateReqModel model = new PayOrderCreateReqModel(); + model.setMchNo(normalMchParams.getMerchantNo()); // 商户号 + model.setAppId(normalMchParams.getAppId()); // 应用ID + model.setMchOrderNo(payOrder.getPayOrderId()); // 商户订单号 + model.setWayCode(JeepluspayConfig.ALI_QR); // 支付方式 + model.setAmount(payOrder.getAmount()); // 金额,单位分 + model.setCurrency(payOrder.getCurrency()); // 币种,目前只支持cny + model.setClientIp(payOrder.getClientIp()); // 发起支付请求客户端的IP地址 + model.setSubject(payOrder.getSubject()); // 商品标题 + model.setBody(payOrder.getBody()); // 商品描述 + model.setNotifyUrl(getNotifyUrl()); // 异步通知地址 + request.setBizModel(model); + // 构造函数响应数据 + AliQrOrderRS res = ApiResBuilder.buildSuccess(AliQrOrderRS.class); + ChannelRetMsg channelRetMsg = new ChannelRetMsg(); + res.setChannelRetMsg(channelRetMsg); + try { + // 发起统一下单 + PayOrderCreateResponse response = new PayOrderCreateResponse(); + boolean checkSign = false; + boolean isSuccess = false; + if (normalMchParams.getSignType().equals(JeepluspayConfig.DEFAULT_SIGN_TYPE) || StringUtils.isEmpty(normalMchParams.getSignType())) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getAppSecret(), Jeepay.getApiBase()); + response = jeepayClient.execute(request); + checkSign = response.checkSign(normalMchParams.getAppSecret()); + isSuccess = response.isSuccess(normalMchParams.getAppSecret()); + + } else if (normalMchParams.getSignType().equals(JeepluspayConfig.SIGN_TYPE_RSA2)) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getRsa2AppPrivateKey(), Jeepay.getApiBase()); + response = jeepayClient.executeByRSA2(request); + checkSign = response.checkSignByRsa2(normalMchParams.getRsa2PayPublicKey()); + isSuccess = response.isSuccessByRsa2(normalMchParams.getRsa2PayPublicKey()); + } + + if (checkSign) { + channelRetMsg.setChannelOrderId(response.get().getPayOrderId()); + if (isSuccess) { + // 下单成功 + // 二维码图片地址 + if (CS.PAY_DATA_TYPE.CODE_IMG_URL.equals(aliQrOrderRQ.getPayDataType())) { + res.setCodeImgUrl(sysConfigService.getDBApplicationConfig().genScanImgUrl(response.get().getPayData())); + } else { + res.setCodeUrl(response.get().getPayData()); + } + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING); + } else { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + channelRetMsg.setChannelErrCode(response.get().getErrCode()); + channelRetMsg.setChannelErrMsg(response.get().getErrMsg()); + } + } + } catch (JeepayException e) { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + } + return res; + } +} \ No newline at end of file diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliWap.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliWap.java new file mode 100644 index 0000000..91356db --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/AliWap.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay.payway; + +import com.jeequan.jeepay.Jeepay; +import com.jeequan.jeepay.JeepayClient; +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.core.entity.PayOrder; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayConfig; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayNormalMchParams; +import com.jeequan.jeepay.exception.JeepayException; +import com.jeequan.jeepay.model.PayOrderCreateReqModel; +import com.jeequan.jeepay.pay.channel.jeepluspay.JeepluspayPaymentService; +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.AliWapOrderRS; +import com.jeequan.jeepay.pay.util.ApiResBuilder; +import com.jeequan.jeepay.request.PayOrderCreateRequest; +import com.jeequan.jeepay.response.PayOrderCreateResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +/* + * 计全付 支付宝 wap支付 + * + * @author yr + * @site https://www.jeequan.com + * @date 2022/8/17 14:46 + */ +@Service("jeepluspayPaymentByAliWapService") //Service Name需保持全局唯一性 +public class AliWap extends JeepluspayPaymentService { + + @Override + public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) { + return null; + } + + @Override + public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) { + JeepluspayNormalMchParams normalMchParams = (JeepluspayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.JEEPLUSPAY); + // 构建请求数据 + PayOrderCreateRequest request = new PayOrderCreateRequest(); + PayOrderCreateReqModel model = new PayOrderCreateReqModel(); + model.setMchNo(normalMchParams.getMerchantNo()); // 商户号 + model.setAppId(normalMchParams.getAppId()); // 应用ID + model.setMchOrderNo(payOrder.getPayOrderId()); // 商户订单号 + model.setWayCode(JeepluspayConfig.ALI_WAP); // 支付方式 + model.setAmount(payOrder.getAmount()); // 金额,单位分 + model.setCurrency(payOrder.getCurrency()); // 币种,目前只支持cny + model.setClientIp(payOrder.getClientIp()); // 发起支付请求客户端的IP地址 + model.setSubject(payOrder.getSubject()); // 商品标题 + model.setBody(payOrder.getBody()); // 商品描述 + model.setNotifyUrl(getNotifyUrl()); // 异步通知地址 + request.setBizModel(model); + // 构造函数响应数据 + AliWapOrderRS res = ApiResBuilder.buildSuccess(AliWapOrderRS.class); + ChannelRetMsg channelRetMsg = new ChannelRetMsg(); + res.setChannelRetMsg(channelRetMsg); + try { + // 发起统一下单 + PayOrderCreateResponse response = new PayOrderCreateResponse(); + boolean checkSign = false; + boolean isSuccess = false; + if (normalMchParams.getSignType().equals(JeepluspayConfig.DEFAULT_SIGN_TYPE) || StringUtils.isEmpty(normalMchParams.getSignType())) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getAppSecret(), Jeepay.getApiBase()); + response = jeepayClient.execute(request); + checkSign = response.checkSign(normalMchParams.getAppSecret()); + isSuccess = response.isSuccess(normalMchParams.getAppSecret()); + + } else if (normalMchParams.getSignType().equals(JeepluspayConfig.SIGN_TYPE_RSA2)) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getRsa2AppPrivateKey(), Jeepay.getApiBase()); + response = jeepayClient.executeByRSA2(request); + checkSign = response.checkSignByRsa2(normalMchParams.getRsa2PayPublicKey()); + isSuccess = response.isSuccessByRsa2(normalMchParams.getRsa2PayPublicKey()); + } + + if (checkSign) { + channelRetMsg.setChannelOrderId(response.get().getPayOrderId()); + if (isSuccess) { + // 下单成功 + String payUrl = response.getData().getString("payData"); + String payDataType = response.getData().getString("payDataType"); + if (CS.PAY_DATA_TYPE.FORM.equals(payDataType)) { //表单方式 + res.setFormContent(payUrl); + } else if (CS.PAY_DATA_TYPE.CODE_IMG_URL.equals(payDataType)) { //二维码图片地址 + res.setCodeImgUrl(payUrl); + } else { // 默认都为 payUrl方式 + res.setPayUrl(payUrl); + } + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING); + } else { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + channelRetMsg.setChannelErrCode(response.get().getErrCode()); + channelRetMsg.setChannelErrMsg(response.get().getErrMsg()); + } + } + } catch (JeepayException e) { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + } + return res; + } +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxApp.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxApp.java new file mode 100644 index 0000000..ff92489 --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxApp.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay.payway; + +import com.alibaba.fastjson.JSONObject; +import com.jeequan.jeepay.Jeepay; +import com.jeequan.jeepay.JeepayClient; +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.core.entity.PayOrder; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayConfig; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayNormalMchParams; +import com.jeequan.jeepay.exception.JeepayException; +import com.jeequan.jeepay.model.PayOrderCreateReqModel; +import com.jeequan.jeepay.pay.channel.jeepluspay.JeepluspayPaymentService; +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.WxAppOrderRS; +import com.jeequan.jeepay.pay.util.ApiResBuilder; +import com.jeequan.jeepay.request.PayOrderCreateRequest; +import com.jeequan.jeepay.response.PayOrderCreateResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +/* + * 计全付 微信 app支付 + * + * @author yr + * @site https://www.jeequan.com + * @date 2022/8/17 15:50 + */ +@Service("jeepluspayPaymentByWxAppService") //Service Name需保持全局唯一性 +public class WxApp extends JeepluspayPaymentService { + + @Override + public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) { + return null; + } + + @Override + public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception { + JeepluspayNormalMchParams normalMchParams = (JeepluspayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.JEEPLUSPAY); + // 构建请求数据 + PayOrderCreateRequest request = new PayOrderCreateRequest(); + PayOrderCreateReqModel model = new PayOrderCreateReqModel(); + model.setMchNo(normalMchParams.getMerchantNo()); // 商户号 + model.setAppId(normalMchParams.getAppId()); // 应用ID + model.setMchOrderNo(payOrder.getPayOrderId()); // 商户订单号 + model.setWayCode(JeepluspayConfig.WX_APP); // 支付方式 + model.setAmount(payOrder.getAmount()); // 金额,单位分 + model.setCurrency(payOrder.getCurrency()); // 币种,目前只支持cny + model.setClientIp(payOrder.getClientIp()); // 发起支付请求客户端的IP地址 + model.setSubject(payOrder.getSubject()); // 商品标题 + model.setBody(payOrder.getBody()); // 商品描述 + model.setNotifyUrl(getNotifyUrl()); // 异步通知地址 + JSONObject channelExtra = new JSONObject(); + channelExtra.put("payDataType", CS.PAY_DATA_TYPE.WX_APP); + model.setChannelExtra(channelExtra.toString()); // 微信app支付参数 + request.setBizModel(model); + // 构造函数响应数据 + WxAppOrderRS res = ApiResBuilder.buildSuccess(WxAppOrderRS.class); + ChannelRetMsg channelRetMsg = new ChannelRetMsg(); + res.setChannelRetMsg(channelRetMsg); + try { + // 发起统一下单 + PayOrderCreateResponse response = new PayOrderCreateResponse(); + boolean checkSign = false; + boolean isSuccess = false; + if (normalMchParams.getSignType().equals(JeepluspayConfig.DEFAULT_SIGN_TYPE) || StringUtils.isEmpty(normalMchParams.getSignType())) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getAppSecret(), Jeepay.getApiBase()); + response = jeepayClient.execute(request); + checkSign = response.checkSign(normalMchParams.getAppSecret()); + isSuccess = response.isSuccess(normalMchParams.getAppSecret()); + + } else if (normalMchParams.getSignType().equals(JeepluspayConfig.SIGN_TYPE_RSA2)) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getRsa2AppPrivateKey(), Jeepay.getApiBase()); + response = jeepayClient.executeByRSA2(request); + checkSign = response.checkSignByRsa2(normalMchParams.getRsa2PayPublicKey()); + isSuccess = response.isSuccessByRsa2(normalMchParams.getRsa2PayPublicKey()); + } + + if (checkSign) { + channelRetMsg.setChannelOrderId(response.get().getPayOrderId()); + if (isSuccess) { + // 下单成功 + JSONObject payData = response.getData().getJSONObject("payData"); + JSONObject resJSON = new JSONObject(); + resJSON.put("package", payData.toJSONString()); + res.setPayInfo(resJSON.toJSONString()); + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING); + } else { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + channelRetMsg.setChannelErrCode(response.get().getErrCode()); + channelRetMsg.setChannelErrMsg(response.get().getErrMsg()); + } + } + } catch (JeepayException e) { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + } + return res; + } +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxBar.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxBar.java new file mode 100644 index 0000000..85de92a --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxBar.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay.payway; + +import com.alibaba.fastjson.JSONObject; +import com.jeequan.jeepay.Jeepay; +import com.jeequan.jeepay.JeepayClient; +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.core.entity.PayOrder; +import com.jeequan.jeepay.core.exception.BizException; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayConfig; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayNormalMchParams; +import com.jeequan.jeepay.exception.JeepayException; +import com.jeequan.jeepay.model.PayOrderCreateReqModel; +import com.jeequan.jeepay.pay.channel.jeepluspay.JeepluspayPaymentService; +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.AliBarOrderRS; +import com.jeequan.jeepay.pay.rqrs.payorder.payway.WxBarOrderRQ; +import com.jeequan.jeepay.pay.util.ApiResBuilder; +import com.jeequan.jeepay.request.PayOrderCreateRequest; +import com.jeequan.jeepay.response.PayOrderCreateResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +/* + * 计全付 微信 bar + * + * @author yr + * @site https://www.jeequan.com + * @date 2022/8/16 18:37 + */ +@Service("jeepluspayPaymentByWxBarService") //Service Name需保持全局唯一性 +public class WxBar extends JeepluspayPaymentService { + + @Override + public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) { + WxBarOrderRQ bizRQ = (WxBarOrderRQ) rq; + if (StringUtils.isEmpty(bizRQ.getAuthCode())) { + throw new BizException("用户支付条码[authCode]不可为空"); + } + return null; + } + + @Override + public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception { + WxBarOrderRQ bizRQ = (WxBarOrderRQ) rq; + JeepluspayNormalMchParams normalMchParams = (JeepluspayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.JEEPLUSPAY); + // 构建请求数据 + PayOrderCreateRequest request = new PayOrderCreateRequest(); + PayOrderCreateReqModel model = new PayOrderCreateReqModel(); + model.setMchNo(normalMchParams.getMerchantNo()); // 商户号 + model.setAppId(normalMchParams.getAppId()); // 应用ID + model.setMchOrderNo(payOrder.getPayOrderId()); // 商户订单号 + model.setWayCode(JeepluspayConfig.WX_BAR); // 支付方式 + model.setAmount(payOrder.getAmount()); // 金额,单位分 + model.setCurrency(payOrder.getCurrency()); // 币种,目前只支持cny + model.setClientIp(payOrder.getClientIp()); // 发起支付请求客户端的IP地址 + model.setSubject(payOrder.getSubject()); // 商品标题 + model.setBody(payOrder.getBody()); // 商品描述 + model.setNotifyUrl(getNotifyUrl()); // 异步通知地址 + JSONObject channelExtra = new JSONObject(); + channelExtra.put("authCode", bizRQ.getAuthCode()); + model.setChannelExtra(channelExtra.toString()); // 用户付款码值 + request.setBizModel(model); + // 构造函数响应数据 + AliBarOrderRS res = ApiResBuilder.buildSuccess(AliBarOrderRS.class); + ChannelRetMsg channelRetMsg = new ChannelRetMsg(); + res.setChannelRetMsg(channelRetMsg); + try { + // 发起统一下单 + PayOrderCreateResponse response = new PayOrderCreateResponse(); + boolean checkSign = false; + boolean isSuccess = false; + if (normalMchParams.getSignType().equals(JeepluspayConfig.DEFAULT_SIGN_TYPE) || StringUtils.isEmpty(normalMchParams.getSignType())) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getAppSecret(), Jeepay.getApiBase()); + response = jeepayClient.execute(request); + checkSign = response.checkSign(normalMchParams.getAppSecret()); + isSuccess = response.isSuccess(normalMchParams.getAppSecret()); + + } else if (normalMchParams.getSignType().equals(JeepluspayConfig.SIGN_TYPE_RSA2)) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getRsa2AppPrivateKey(), Jeepay.getApiBase()); + response = jeepayClient.executeByRSA2(request); + checkSign = response.checkSignByRsa2(normalMchParams.getRsa2PayPublicKey()); + isSuccess = response.isSuccessByRsa2(normalMchParams.getRsa2PayPublicKey()); + } + + if (checkSign) { + channelRetMsg.setChannelOrderId(response.get().getPayOrderId()); + if (isSuccess) { + // 下单成功 + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS); + } else { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + channelRetMsg.setChannelErrCode(response.get().getErrCode()); + channelRetMsg.setChannelErrMsg(response.get().getErrMsg()); + } + } + } catch (JeepayException e) { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + } + return res; + } +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxH5.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxH5.java new file mode 100644 index 0000000..6d949af --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxH5.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay.payway; + +import com.jeequan.jeepay.Jeepay; +import com.jeequan.jeepay.JeepayClient; +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.core.entity.PayOrder; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayConfig; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayNormalMchParams; +import com.jeequan.jeepay.exception.JeepayException; +import com.jeequan.jeepay.model.PayOrderCreateReqModel; +import com.jeequan.jeepay.pay.channel.jeepluspay.JeepluspayPaymentService; +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.WxH5OrderRS; +import com.jeequan.jeepay.pay.util.ApiResBuilder; +import com.jeequan.jeepay.request.PayOrderCreateRequest; +import com.jeequan.jeepay.response.PayOrderCreateResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +/* + * 计全付 微信 H5 支付 + * + * @author yr + * @site https://www.jeequan.com + * @date 2022/8/17 15:37 + */ +@Service("jeepluspayPaymentByWxH5Service") //Service Name需保持全局唯一性 +public class WxH5 extends JeepluspayPaymentService { + + @Override + public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) { + return null; + } + + @Override + public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) { + JeepluspayNormalMchParams normalMchParams = (JeepluspayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.JEEPLUSPAY); + // 构建请求数据 + PayOrderCreateRequest request = new PayOrderCreateRequest(); + PayOrderCreateReqModel model = new PayOrderCreateReqModel(); + model.setMchNo(normalMchParams.getMerchantNo()); // 商户号 + model.setAppId(normalMchParams.getAppId()); // 应用ID + model.setMchOrderNo(payOrder.getPayOrderId()); // 商户订单号 + model.setWayCode(JeepluspayConfig.WX_H5); // 支付方式 + model.setAmount(payOrder.getAmount()); // 金额,单位分 + model.setCurrency(payOrder.getCurrency()); // 币种,目前只支持cny + model.setClientIp(payOrder.getClientIp()); // 发起支付请求客户端的IP地址 + model.setSubject(payOrder.getSubject()); // 商品标题 + model.setBody(payOrder.getBody()); // 商品描述 + model.setNotifyUrl(getNotifyUrl()); // 异步通知地址 + request.setBizModel(model); + // 构造函数响应数据 + WxH5OrderRS res = ApiResBuilder.buildSuccess(WxH5OrderRS.class); + ChannelRetMsg channelRetMsg = new ChannelRetMsg(); + res.setChannelRetMsg(channelRetMsg); + try { + // 发起统一下单 + PayOrderCreateResponse response = new PayOrderCreateResponse(); + boolean checkSign = false; + boolean isSuccess = false; + if (normalMchParams.getSignType().equals(JeepluspayConfig.DEFAULT_SIGN_TYPE) || StringUtils.isEmpty(normalMchParams.getSignType())) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getAppSecret(), Jeepay.getApiBase()); + response = jeepayClient.execute(request); + checkSign = response.checkSign(normalMchParams.getAppSecret()); + isSuccess = response.isSuccess(normalMchParams.getAppSecret()); + + } else if (normalMchParams.getSignType().equals(JeepluspayConfig.SIGN_TYPE_RSA2)) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getRsa2AppPrivateKey(), Jeepay.getApiBase()); + response = jeepayClient.executeByRSA2(request); + checkSign = response.checkSignByRsa2(normalMchParams.getRsa2PayPublicKey()); + isSuccess = response.isSuccessByRsa2(normalMchParams.getRsa2PayPublicKey()); + } + + if (checkSign) { + channelRetMsg.setChannelOrderId(response.get().getPayOrderId()); + if (isSuccess) { + // 下单成功 + String payUrl = response.getData().getString("payData"); + String payDataType = response.getData().getString("payDataType"); + if (CS.PAY_DATA_TYPE.FORM.equals(payDataType)) { //表单方式 + res.setFormContent(payUrl); + } else if (CS.PAY_DATA_TYPE.CODE_IMG_URL.equals(payDataType)) { //二维码图片地址 + res.setCodeImgUrl(payUrl); + } else { // 默认都为 payUrl方式 + res.setPayUrl(payUrl); + } + // 支付中 + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING); + } else { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + channelRetMsg.setChannelErrCode(response.get().getErrCode()); + channelRetMsg.setChannelErrMsg(response.get().getErrMsg()); + } + } + } catch (JeepayException e) { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + } + return res; + } +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxJsapi.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxJsapi.java new file mode 100644 index 0000000..6b303f7 --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxJsapi.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay.payway; + +import com.alibaba.fastjson.JSONObject; +import com.jeequan.jeepay.Jeepay; +import com.jeequan.jeepay.JeepayClient; +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.core.entity.PayOrder; +import com.jeequan.jeepay.core.exception.BizException; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayConfig; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayNormalMchParams; +import com.jeequan.jeepay.exception.JeepayException; +import com.jeequan.jeepay.model.PayOrderCreateReqModel; +import com.jeequan.jeepay.pay.channel.jeepluspay.JeepluspayPaymentService; +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.WxJsapiOrderRQ; +import com.jeequan.jeepay.pay.rqrs.payorder.payway.WxJsapiOrderRS; +import com.jeequan.jeepay.pay.util.ApiResBuilder; +import com.jeequan.jeepay.request.PayOrderCreateRequest; +import com.jeequan.jeepay.response.PayOrderCreateResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +/* + * 计全付 微信 jsapi支付 + * + * @author yr + * @site https://www.jeequan.com + * @date 2022/8/17 14:24 + */ +@Service("jeepluspayPaymentByWxJsapiService") //Service Name需保持全局唯一性 +@Slf4j +public class WxJsapi extends JeepluspayPaymentService { + + @Override + public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) { + WxJsapiOrderRQ bizRQ = (WxJsapiOrderRQ) rq; + if (StringUtils.isEmpty(bizRQ.getOpenid())) { + throw new BizException("[openid]不可为空"); + } + return null; + } + + @Override + public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception { + WxJsapiOrderRQ bizRQ = (WxJsapiOrderRQ) rq; + JeepluspayNormalMchParams normalMchParams = (JeepluspayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.JEEPLUSPAY); + // 构建请求数据 + PayOrderCreateRequest request = new PayOrderCreateRequest(); + PayOrderCreateReqModel model = new PayOrderCreateReqModel(); + model.setMchNo(normalMchParams.getMerchantNo()); // 商户号 + model.setAppId(normalMchParams.getAppId()); // 应用ID + model.setMchOrderNo(payOrder.getPayOrderId()); // 商户订单号 + model.setWayCode(JeepluspayConfig.WX_JSAPI); // 支付方式 + model.setAmount(payOrder.getAmount()); // 金额,单位分 + model.setCurrency(payOrder.getCurrency()); // 币种,目前只支持cny + model.setClientIp(payOrder.getClientIp()); // 发起支付请求客户端的IP地址 + model.setSubject(payOrder.getSubject()); // 商品标题 + model.setBody(payOrder.getBody()); // 商品描述 + model.setNotifyUrl(getNotifyUrl()); // 异步通知地址 + JSONObject channelExtra = new JSONObject(); + channelExtra.put("openid", bizRQ.getOpenid()); + model.setChannelExtra(channelExtra.toString()); // 微信openId + request.setBizModel(model); + // 构造函数响应数据 + WxJsapiOrderRS res = ApiResBuilder.buildSuccess(WxJsapiOrderRS.class); + ChannelRetMsg channelRetMsg = new ChannelRetMsg(); + res.setChannelRetMsg(channelRetMsg); + try { + // 发起统一下单 + PayOrderCreateResponse response = new PayOrderCreateResponse(); + boolean checkSign = false; + boolean isSuccess = false; + if (normalMchParams.getSignType().equals(JeepluspayConfig.DEFAULT_SIGN_TYPE) || StringUtils.isEmpty(normalMchParams.getSignType())) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getAppSecret(), Jeepay.getApiBase()); + response = jeepayClient.execute(request); + checkSign = response.checkSign(normalMchParams.getAppSecret()); + isSuccess = response.isSuccess(normalMchParams.getAppSecret()); + + } else if (normalMchParams.getSignType().equals(JeepluspayConfig.SIGN_TYPE_RSA2)) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getRsa2AppPrivateKey(), Jeepay.getApiBase()); + response = jeepayClient.executeByRSA2(request); + checkSign = response.checkSignByRsa2(normalMchParams.getRsa2PayPublicKey()); + isSuccess = response.isSuccessByRsa2(normalMchParams.getRsa2PayPublicKey()); + } + + if (checkSign) { + channelRetMsg.setChannelOrderId(response.get().getPayOrderId()); + if (isSuccess) { + // 下单成功 + JSONObject payData = response.getData().getJSONObject("payData"); + res.setPayInfo(payData.toJSONString()); + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING); + } else { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + channelRetMsg.setChannelErrCode(response.get().getErrCode()); + channelRetMsg.setChannelErrMsg(response.get().getErrMsg()); + } + } + } catch (JeepayException e) { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + } + return res; + } +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxLite.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxLite.java new file mode 100644 index 0000000..fc75e2b --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxLite.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay.payway; + +import com.alibaba.fastjson.JSONObject; +import com.jeequan.jeepay.Jeepay; +import com.jeequan.jeepay.JeepayClient; +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.core.entity.PayOrder; +import com.jeequan.jeepay.core.exception.BizException; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayConfig; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayNormalMchParams; +import com.jeequan.jeepay.exception.JeepayException; +import com.jeequan.jeepay.model.PayOrderCreateReqModel; +import com.jeequan.jeepay.pay.channel.jeepluspay.JeepluspayPaymentService; +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.WxLiteOrderRQ; +import com.jeequan.jeepay.pay.rqrs.payorder.payway.WxLiteOrderRS; +import com.jeequan.jeepay.pay.util.ApiResBuilder; +import com.jeequan.jeepay.request.PayOrderCreateRequest; +import com.jeequan.jeepay.response.PayOrderCreateResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +/* + * 计全付 微信 小程序支付 + * + * @author yr + * @site https://www.jeequan.com + * @date 2022/8/17 15:24 + */ +@Service("jeepluspayPaymentByWxLiteService") //Service Name需保持全局唯一性 +@Slf4j +public class WxLite extends JeepluspayPaymentService { + + @Override + public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) { + WxLiteOrderRQ bizRQ = (WxLiteOrderRQ) rq; + if (StringUtils.isEmpty(bizRQ.getOpenid())) { + throw new BizException("[openid]不可为空"); + } + return null; + } + + @Override + public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception { + WxLiteOrderRQ bizRQ = (WxLiteOrderRQ) rq; + JeepluspayNormalMchParams normalMchParams = (JeepluspayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.JEEPLUSPAY); + // 构建请求数据 + PayOrderCreateRequest request = new PayOrderCreateRequest(); + PayOrderCreateReqModel model = new PayOrderCreateReqModel(); + model.setMchNo(normalMchParams.getMerchantNo()); // 商户号 + model.setAppId(normalMchParams.getAppId()); // 应用ID + model.setMchOrderNo(payOrder.getPayOrderId()); // 商户订单号 + model.setWayCode(JeepluspayConfig.WX_LITE); // 支付方式 + model.setAmount(payOrder.getAmount()); // 金额,单位分 + model.setCurrency(payOrder.getCurrency()); // 币种,目前只支持cny + model.setClientIp(payOrder.getClientIp()); // 发起支付请求客户端的IP地址 + model.setSubject(payOrder.getSubject()); // 商品标题 + model.setBody(payOrder.getBody()); // 商品描述 + model.setNotifyUrl(getNotifyUrl()); // 异步通知地址 + JSONObject channelExtra = new JSONObject(); + channelExtra.put("openid", bizRQ.getOpenid()); + model.setChannelExtra(channelExtra.toString()); // 微信openId + request.setBizModel(model); + // 构造函数响应数据 + WxLiteOrderRS res = ApiResBuilder.buildSuccess(WxLiteOrderRS.class); + ChannelRetMsg channelRetMsg = new ChannelRetMsg(); + res.setChannelRetMsg(channelRetMsg); + try { + // 发起统一下单 + PayOrderCreateResponse response = new PayOrderCreateResponse(); + boolean checkSign = false; + boolean isSuccess = false; + if (normalMchParams.getSignType().equals(JeepluspayConfig.DEFAULT_SIGN_TYPE) || StringUtils.isEmpty(normalMchParams.getSignType())) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getAppSecret(), Jeepay.getApiBase()); + response = jeepayClient.execute(request); + checkSign = response.checkSign(normalMchParams.getAppSecret()); + isSuccess = response.isSuccess(normalMchParams.getAppSecret()); + + } else if (normalMchParams.getSignType().equals(JeepluspayConfig.SIGN_TYPE_RSA2)) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getRsa2AppPrivateKey(), Jeepay.getApiBase()); + response = jeepayClient.executeByRSA2(request); + checkSign = response.checkSignByRsa2(normalMchParams.getRsa2PayPublicKey()); + isSuccess = response.isSuccessByRsa2(normalMchParams.getRsa2PayPublicKey()); + } + + if (checkSign) { + channelRetMsg.setChannelOrderId(response.get().getPayOrderId()); + if (isSuccess) { + // 下单成功 + JSONObject payData = response.getData().getJSONObject("payData"); + res.setPayInfo(payData.toJSONString()); + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING); + } else { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + channelRetMsg.setChannelErrCode(response.get().getErrCode()); + channelRetMsg.setChannelErrMsg(response.get().getErrMsg()); + } + } + } catch (JeepayException e) { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + } + return res; + } +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxNative.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxNative.java new file mode 100644 index 0000000..c6a6e40 --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/jeepluspay/payway/WxNative.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.jeepluspay.payway; + +import com.jeequan.jeepay.Jeepay; +import com.jeequan.jeepay.JeepayClient; +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.core.entity.PayOrder; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayConfig; +import com.jeequan.jeepay.core.model.params.jeepluspay.JeepluspayNormalMchParams; +import com.jeequan.jeepay.exception.JeepayException; +import com.jeequan.jeepay.model.PayOrderCreateReqModel; +import com.jeequan.jeepay.pay.channel.jeepluspay.JeepluspayPaymentService; +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.WxNativeOrderRQ; +import com.jeequan.jeepay.pay.rqrs.payorder.payway.WxNativeOrderRS; +import com.jeequan.jeepay.pay.util.ApiResBuilder; +import com.jeequan.jeepay.request.PayOrderCreateRequest; +import com.jeequan.jeepay.response.PayOrderCreateResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +/* + * 计全付 微信 native支付 + * + * @author yr + * @site https://www.jeequan.com + * @date 2022/8/11 15:37 + */ +@Service("jeepluspayPaymentByWxNativeService") //Service Name需保持全局唯一性 +public class WxNative extends JeepluspayPaymentService { + + @Override + public String preCheck(UnifiedOrderRQ rq, PayOrder payOrder) { + return null; + } + + @Override + public AbstractRS pay(UnifiedOrderRQ rq, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) throws Exception { + WxNativeOrderRQ bizRQ = (WxNativeOrderRQ) rq; + JeepluspayNormalMchParams normalMchParams = (JeepluspayNormalMchParams) configContextQueryService.queryNormalMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.JEEPLUSPAY); + // 构建请求数据 + PayOrderCreateRequest request = new PayOrderCreateRequest(); + PayOrderCreateReqModel model = new PayOrderCreateReqModel(); + model.setMchNo(normalMchParams.getMerchantNo()); // 商户号 + model.setAppId(normalMchParams.getAppId()); // 应用ID + model.setMchOrderNo(payOrder.getPayOrderId()); // 商户订单号 + model.setWayCode(JeepluspayConfig.WX_NATIVE); // 支付方式 + model.setAmount(payOrder.getAmount()); // 金额,单位分 + model.setCurrency(payOrder.getCurrency()); // 币种,目前只支持cny + model.setClientIp(payOrder.getClientIp()); // 发起支付请求客户端的IP地址 + model.setSubject(payOrder.getSubject()); // 商品标题 + model.setBody(payOrder.getBody()); // 商品描述 + model.setNotifyUrl(getNotifyUrl()); // 异步通知地址 + request.setBizModel(model); + // 构造函数响应数据 + WxNativeOrderRS res = ApiResBuilder.buildSuccess(WxNativeOrderRS.class); + ChannelRetMsg channelRetMsg = new ChannelRetMsg(); + res.setChannelRetMsg(channelRetMsg); + try { + // 发起统一下单 + PayOrderCreateResponse response = new PayOrderCreateResponse(); + boolean checkSign = false; + boolean isSuccess = false; + if (normalMchParams.getSignType().equals(JeepluspayConfig.DEFAULT_SIGN_TYPE) || StringUtils.isEmpty(normalMchParams.getSignType())) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getAppSecret(), Jeepay.getApiBase()); + response = jeepayClient.execute(request); + checkSign = response.checkSign(normalMchParams.getAppSecret()); + isSuccess = response.isSuccess(normalMchParams.getAppSecret()); + + } else if (normalMchParams.getSignType().equals(JeepluspayConfig.SIGN_TYPE_RSA2)) { + JeepayClient jeepayClient = JeepayClient.getInstance(normalMchParams.getAppId(), normalMchParams.getRsa2AppPrivateKey(), Jeepay.getApiBase()); + response = jeepayClient.executeByRSA2(request); + checkSign = response.checkSignByRsa2(normalMchParams.getRsa2PayPublicKey()); + isSuccess = response.isSuccessByRsa2(normalMchParams.getRsa2PayPublicKey()); + } + + if (checkSign) { + channelRetMsg.setChannelOrderId(response.get().getPayOrderId()); + if (isSuccess) { + // 下单成功 + // 二维码图片地址 + if (CS.PAY_DATA_TYPE.CODE_IMG_URL.equals(bizRQ.getPayDataType())) { + res.setCodeImgUrl(sysConfigService.getDBApplicationConfig().genScanImgUrl(response.get().getPayData())); + } else { + res.setCodeUrl(response.get().getPayData()); + } + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.WAITING); + } else { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + channelRetMsg.setChannelErrCode(response.get().getErrCode()); + channelRetMsg.setChannelErrMsg(response.get().getErrMsg()); + } + } + } catch (JeepayException e) { + channelRetMsg.setChannelState(ChannelRetMsg.ChannelState.CONFIRM_FAIL); + } + return res; + } +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/xxpay/payway/WxBar.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/xxpay/payway/WxBar.java index 978129a..6e3f0af 100644 --- a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/xxpay/payway/WxBar.java +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/channel/xxpay/payway/WxBar.java @@ -23,8 +23,8 @@ 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.AliBarOrderRS; import com.jeequan.jeepay.pay.rqrs.payorder.payway.WxBarOrderRQ; +import com.jeequan.jeepay.pay.rqrs.payorder.payway.WxBarOrderRS; import com.jeequan.jeepay.pay.util.ApiResBuilder; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -72,7 +72,7 @@ public class WxBar extends XxpayPaymentService { paramMap.put("body", payOrder.getBody()); paramMap.put("extra", bizRQ.getAuthCode()); // 构造函数响应数据 - AliBarOrderRS res = ApiResBuilder.buildSuccess(AliBarOrderRS.class); + WxBarOrderRS res = ApiResBuilder.buildSuccess(WxBarOrderRS.class); ChannelRetMsg channelRetMsg = new ChannelRetMsg(); res.setChannelRetMsg(channelRetMsg); // 发起支付 diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/UnifiedOrderRQ.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/UnifiedOrderRQ.java index d3d940e..d9b4f6b 100644 --- a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/UnifiedOrderRQ.java +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/UnifiedOrderRQ.java @@ -98,6 +98,10 @@ public class UnifiedOrderRQ extends AbstractMchAppRQ { AliJsapiOrderRQ bizRQ = JSONObject.parseObject(StringUtils.defaultIfEmpty(this.channelExtra, "{}"), AliJsapiOrderRQ.class); BeanUtils.copyProperties(this, bizRQ); return bizRQ; + }else if(CS.PAY_WAY_CODE.ALI_LITE.equals(wayCode)){ + AliLiteOrderRQ bizRQ = JSONObject.parseObject(StringUtils.defaultIfEmpty(this.channelExtra, "{}"), AliLiteOrderRQ.class); + BeanUtils.copyProperties(this, bizRQ); + return bizRQ; }else if(CS.PAY_WAY_CODE.QR_CASHIER.equals(wayCode)){ QrCashierOrderRQ bizRQ = JSONObject.parseObject(StringUtils.defaultIfEmpty(this.channelExtra, "{}"), QrCashierOrderRQ.class); BeanUtils.copyProperties(this, bizRQ); @@ -106,6 +110,10 @@ public class UnifiedOrderRQ extends AbstractMchAppRQ { WxJsapiOrderRQ bizRQ = JSONObject.parseObject(StringUtils.defaultIfEmpty(this.channelExtra, "{}"), WxJsapiOrderRQ.class); BeanUtils.copyProperties(this, bizRQ); return bizRQ; + }else if(CS.PAY_WAY_CODE.WX_LITE.equals(wayCode)){ + WxLiteOrderRQ bizRQ = JSONObject.parseObject(StringUtils.defaultIfEmpty(this.channelExtra, "{}"), WxLiteOrderRQ.class); + BeanUtils.copyProperties(this, bizRQ); + return bizRQ; }else if(CS.PAY_WAY_CODE.WX_BAR.equals(wayCode)){ WxBarOrderRQ bizRQ = JSONObject.parseObject(StringUtils.defaultIfEmpty(this.channelExtra, "{}"), WxBarOrderRQ.class); BeanUtils.copyProperties(this, bizRQ); diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/payway/AliLiteOrderRQ.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/payway/AliLiteOrderRQ.java new file mode 100644 index 0000000..1500965 --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/payway/AliLiteOrderRQ.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.rqrs.payorder.payway; + +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.pay.rqrs.payorder.UnifiedOrderRQ; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/* + * 支付方式: ALI_LITE + * + * @author terrfly + * @site https://www.jeequan.com + * @date 2021/6/8 17:34 + */ +@Data +public class AliLiteOrderRQ extends UnifiedOrderRQ { + + /** 支付宝用户ID **/ + @NotBlank(message = "用户ID不能为空") + private String buyerUserId; + + /** 构造函数 **/ + public AliLiteOrderRQ(){ + this.setWayCode(CS.PAY_WAY_CODE.ALI_LITE); + } + + @Override + public String getChannelUserId(){ + return this.buyerUserId; + } + +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/payway/AliLiteOrderRS.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/payway/AliLiteOrderRS.java new file mode 100644 index 0000000..a32c3b1 --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/payway/AliLiteOrderRS.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.rqrs.payorder.payway; + +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.core.utils.JsonKit; +import com.jeequan.jeepay.pay.rqrs.payorder.UnifiedOrderRS; +import lombok.Data; + +/* + * 支付方式: ALI_LITE + * + * @author terrfly + * @site https://www.jeequan.com + * @date 2021/6/8 17:34 + */ +@Data +public class AliLiteOrderRS extends UnifiedOrderRS { + + /** 调起支付插件的支付宝订单号 **/ + private String alipayTradeNo; + + @Override + public String buildPayDataType(){ + return CS.PAY_DATA_TYPE.ALI_APP; + } + + @Override + public String buildPayData(){ + return JsonKit.newJson("alipayTradeNo", alipayTradeNo).toString(); + } + +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/payway/WxLiteOrderRQ.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/payway/WxLiteOrderRQ.java new file mode 100644 index 0000000..81d5d12 --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/payway/WxLiteOrderRQ.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.rqrs.payorder.payway; + +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.pay.rqrs.payorder.UnifiedOrderRQ; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/* + * 支付方式: WX_LITE + * + * @author zhuxiao + * @site https://www.jeequan.com + * @date 2021/6/8 17:34 + */ +@Data +public class WxLiteOrderRQ extends UnifiedOrderRQ { + + /** 微信openid **/ + @NotBlank(message = "openid不能为空") + private String openid; + + /** 标志是否为 subMchLiteAppId的对应 openId, 0-否, 1-是, 默认否 **/ + private Byte isSubOpenId; + + /** 构造函数 **/ + public WxLiteOrderRQ(){ + this.setWayCode(CS.PAY_WAY_CODE.WX_LITE); + } + + @Override + public String getChannelUserId() { + return this.openid; + } +} diff --git a/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/payway/WxLiteOrderRS.java b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/payway/WxLiteOrderRS.java new file mode 100644 index 0000000..71cbedf --- /dev/null +++ b/jeepay-payment/src/main/java/com/jeequan/jeepay/pay/rqrs/payorder/payway/WxLiteOrderRS.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021-2031, 河北计全科技有限公司 (https://www.jeequan.com & jeequan@126.com). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.rqrs.payorder.payway; + +import com.jeequan.jeepay.core.constants.CS; +import com.jeequan.jeepay.pay.rqrs.payorder.UnifiedOrderRS; +import lombok.Data; + +/* + * 支付方式: WX_LITE + * + * @author zhuxiao + * @site https://www.jeequan.com + * @date 2021/6/8 17:34 + */ +@Data +public class WxLiteOrderRS extends UnifiedOrderRS { + + /** 预支付数据包 **/ + private String payInfo; + + @Override + public String buildPayDataType(){ + return CS.PAY_DATA_TYPE.WX_APP; + } + + @Override + public String buildPayData(){ + return payInfo; + } + +} diff --git a/libs/jeepay-sdk-java-pls-1.0.0.jar b/libs/jeepay-sdk-java-pls-1.0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..9cb7c5b1f36099971568a40672bdad5b42e2e309 GIT binary patch literal 90726 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