commit b26620cdf0556ba2a5961bfcd27ad5e5ae314fca Author: tiangao Date: Sat Aug 30 15:02:10 2025 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..269f5c9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +#*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +.vscode + +.idea +*.iml + +.DS_Store + +.env + +target + +build + +dist + +node_modules + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..5a54969 --- /dev/null +++ b/pom.xml @@ -0,0 +1,102 @@ + + + 4.0.0 + com.lakala.moss + moss-sdk + 1.0.0 + moss-sdk + moss-sdk + + UTF-8 + 1.8 + 1.8 + + + + org.bouncycastle + bcprov-jdk15on + 1.64 + + + commons-httpclient + commons-httpclient + 3.1 + + + org.slf4j + slf4j-api + 1.7.32 + + + org.slf4j + slf4j-simple + 1.7.25 + + + com.google.code.gson + gson + 2.8.7 + + + commons-codec + commons-codec + 1.15 + + + org.apache.httpcomponents + httpclient + 4.5.13 + + + commons-lang + commons-lang + 2.5 + + + junit + junit + 4.11 + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.3 + + ${maven.compiler.source} + ${maven.compiler.target} + ${project.build.sourceEncoding} + + + + + maven-assembly-plugin + + + + . + + + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + + diff --git a/src/main/java/com/lakala/moss/api/ApiReq.java b/src/main/java/com/lakala/moss/api/ApiReq.java new file mode 100644 index 0000000..bb271ce --- /dev/null +++ b/src/main/java/com/lakala/moss/api/ApiReq.java @@ -0,0 +1,52 @@ +package com.lakala.moss.api; + + +/** + * @author Stephen yu + * @description: 请求消息-消息公共参数 + * @date 2025-02-21 + */ +public class ApiReq { + + // 请求消息头 + private ApiReqHead head; + // 请求消息体 + private T request; + // 请求消息体密文 + private String requestEncrypted; + // 请求签名 + private String sign; + + public ApiReqHead getHead() { + return head; + } + + public void setHead(ApiReqHead head) { + this.head = head; + } + + public T getRequest() { + return request; + } + + public void setRequest(T request) { + this.request = request; + } + + public String getRequestEncrypted() { + return requestEncrypted; + } + + public void setRequestEncrypted(String requestEncrypted) { + this.requestEncrypted = requestEncrypted; + } + + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } + +} diff --git a/src/main/java/com/lakala/moss/api/ApiReqHead.java b/src/main/java/com/lakala/moss/api/ApiReqHead.java new file mode 100644 index 0000000..4c04051 --- /dev/null +++ b/src/main/java/com/lakala/moss/api/ApiReqHead.java @@ -0,0 +1,98 @@ +package com.lakala.moss.api; + +/** + * @author Stephen yu + * @description: 请求消息-消息头 + * @date 2025-02-21 + */ +public class ApiReqHead { + + // 版本号,值为1.0 + private String versionId; + // 交易服务码 + private String serviceId; + // 技术渠道号,值为API + private String channelId; + // 请求时间,格式:yyyyMMddHHmmss + private String requestTime; + // 交易流水号,请求方生成,建议使用32位UUID + private String serviceSn; + // 业务渠道号,即为MOSS为客户生成的唯一客户编号(同为开发应用Appid,在工作台-开发者中心获取) + private String businessChannel; + + private String sid; + private String org; + private String accessToken; + + public String getVersionId() { + return versionId; + } + + public void setVersionId(String versionId) { + this.versionId = versionId; + } + + public String getServiceId() { + return serviceId; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public String getRequestTime() { + return requestTime; + } + + public void setRequestTime(String requestTime) { + this.requestTime = requestTime; + } + + public String getServiceSn() { + return serviceSn; + } + + public void setServiceSn(String serviceSn) { + this.serviceSn = serviceSn; + } + + public String getBusinessChannel() { + return businessChannel; + } + + public void setBusinessChannel(String businessChannel) { + this.businessChannel = businessChannel; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + + public String getOrg() { + return org; + } + + public void setOrg(String org) { + this.org = org; + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } +} diff --git a/src/main/java/com/lakala/moss/api/ApiRes.java b/src/main/java/com/lakala/moss/api/ApiRes.java new file mode 100644 index 0000000..9af584b --- /dev/null +++ b/src/main/java/com/lakala/moss/api/ApiRes.java @@ -0,0 +1,50 @@ +package com.lakala.moss.api; + +/** + * @author Stephen yu + * @description: 消息消息-消息公共参数 + * @date @date 2025-02-21 + */ +public class ApiRes { + + // 响应消息头 + private ApiResHead head; + // 响应消息体 + private T response; + // 响应消息体密文 + private String responseEncrypted; + // 响应签名 + private String sign; + + public ApiResHead getHead() { + return head; + } + + public void setHead(ApiResHead head) { + this.head = head; + } + + public T getResponse() { + return response; + } + + public void setResponse(T response) { + this.response = response; + } + + public String getResponseEncrypted() { + return responseEncrypted; + } + + public void setResponseEncrypted(String responseEncrypted) { + this.responseEncrypted = responseEncrypted; + } + + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } +} diff --git a/src/main/java/com/lakala/moss/api/ApiResCode.java b/src/main/java/com/lakala/moss/api/ApiResCode.java new file mode 100644 index 0000000..96e40c2 --- /dev/null +++ b/src/main/java/com/lakala/moss/api/ApiResCode.java @@ -0,0 +1,33 @@ +package com.lakala.moss.api; + +/** + * @author Stephen yu + * @description: 接口结果码定义 + * @date 2025-02-21 + */ +public enum ApiResCode { + SUCCESS("000000", "交易成功"), + PA_INSIDE_ERROR("MOSS0001", "交易处理失败"), + PA_TIMEOUT_ERROR("MOSS0002", "交易通讯异常"), + PA_UNKNOWN_ERROR("MOSS0003", "交易处理异常"), + PA_PARAM_ERROR("MOSS0004", "参数校验错误"), + PA_REPEAT_ERROR("MOSS0005", "订单重复"), + PA_NOEXIST_ERROR("MOSS0006", "订单不存在") + ; + + private String code; + private String desc; + + ApiResCode(final String code, final String desc) { + this.code = code; + this.desc = desc; + } + + public String getCode() { + return this.code; + } + + public String getDesc() { + return this.desc; + } +} diff --git a/src/main/java/com/lakala/moss/api/ApiResHead.java b/src/main/java/com/lakala/moss/api/ApiResHead.java new file mode 100644 index 0000000..593ad00 --- /dev/null +++ b/src/main/java/com/lakala/moss/api/ApiResHead.java @@ -0,0 +1,70 @@ +package com.lakala.moss.api; + +/** + * @author Stephen yu + * @description: 响应消息-消息头 + * @date 2025-02-21 + */ +public class ApiResHead { + + // 响应码,8位字符,000000为成功,其他为失败 + private String code; + // 响应码描述 + private String desc; + // 响应时间 + private String serviceTime; + // 响应流水号,原值返回 + private String serviceSn; + // 交易服务码 + private String serviceId; + // 全局流水 + private String sid; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getServiceTime() { + return serviceTime; + } + + public void setServiceTime(String serviceTime) { + this.serviceTime = serviceTime; + } + + public String getServiceSn() { + return serviceSn; + } + + public void setServiceSn(String serviceSn) { + this.serviceSn = serviceSn; + } + + public String getServiceId() { + return serviceId; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } +} diff --git a/src/main/java/com/lakala/moss/api/params/AccBusiFields.java b/src/main/java/com/lakala/moss/api/params/AccBusiFields.java new file mode 100644 index 0000000..28b5c51 --- /dev/null +++ b/src/main/java/com/lakala/moss/api/params/AccBusiFields.java @@ -0,0 +1,175 @@ +package com.lakala.moss.api.params; + +import javax.xml.soap.Detail; + +/** + * 请求参数-账户端业务信息域 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class AccBusiFields { + // 业务扩展参数 + private ExtendParams extend_params; + // 商品详情 + private GoodsDetail goods_detail; + // 商户门店编号 + private String store_id; + // 支付宝禁用支付渠道 credit_group 表示禁用信用支付类(包含信用卡,花呗,花呗分期) pcredit 表示禁用花呗” + // pcreditpayInstallment 表示禁用花呗分期 creditCard 表示禁用信用卡 如果想禁用多个可在枚举间加,隔开 + private String disable_pay_channels; + // 商户传入业务信息,应用于安全,营销等参数直传场景,格式为 json 格式。 + // 示例:{“enable_thirdparty_subsidy”:”N”,”source”:”xxxx”},source送值与吱口令生成接口中上送的source对应, + // 值内容为“inderict_wx_吱口令申领接口中source值” + private String business_params; + // 微信分配的子商户公众账号ID,即微信小程序支付-71、公众号支付-51、此参数必传 + private String sub_appid; + // 商品详情 + private Detail detail; + // 附加域,该字段主要用于商户携带订单的自定义数据。商户定制字段,直接送到账户端。 + private String attach; + // 终端设备号(门店号或收银设备ID),注意:PC网页或JSAPI支付请传”WEB” + private String device_info; + // 指定支付方式,no_credit–指定不能使用信用卡支付 + private String limit_pay; + // 场景信息 + private String scene_info; + // 银联单品营销订单信息 + private AcqAddnDataOrderInfo acq_addn_data_order_info; + // 银联单品营销商品信息 + private AcqAddnDataGoodsInfo acq_addn_data_goods_info; + // 银联前台通知地址 + private String front_url; + // 银联失败交易前台通知地址 + private String front_fail_url; + // 用户信息的临时授权码 + private String user_auth_code; + + public ExtendParams getExtend_params() { + return extend_params; + } + + public void setExtend_params(ExtendParams extend_params) { + this.extend_params = extend_params; + } + + public GoodsDetail getGoods_detail() { + return goods_detail; + } + + public void setGoods_detail(GoodsDetail goods_detail) { + this.goods_detail = goods_detail; + } + + public String getStore_id() { + return store_id; + } + + public void setStore_id(String store_id) { + this.store_id = store_id; + } + + public String getDisable_pay_channels() { + return disable_pay_channels; + } + + public void setDisable_pay_channels(String disable_pay_channels) { + this.disable_pay_channels = disable_pay_channels; + } + + public String getBusiness_params() { + return business_params; + } + + public void setBusiness_params(String business_params) { + this.business_params = business_params; + } + + public String getSub_appid() { + return sub_appid; + } + + public void setSub_appid(String sub_appid) { + this.sub_appid = sub_appid; + } + + public Detail getDetail() { + return detail; + } + + public void setDetail(Detail detail) { + this.detail = detail; + } + + public String getAttach() { + return attach; + } + + public void setAttach(String attach) { + this.attach = attach; + } + + public String getDevice_info() { + return device_info; + } + + public void setDevice_info(String device_info) { + this.device_info = device_info; + } + + public String getLimit_pay() { + return limit_pay; + } + + public void setLimit_pay(String limit_pay) { + this.limit_pay = limit_pay; + } + + public String getScene_info() { + return scene_info; + } + + public void setScene_info(String scene_info) { + this.scene_info = scene_info; + } + + public AcqAddnDataOrderInfo getAcq_addn_data_order_info() { + return acq_addn_data_order_info; + } + + public void setAcq_addn_data_order_info(AcqAddnDataOrderInfo acq_addn_data_order_info) { + this.acq_addn_data_order_info = acq_addn_data_order_info; + } + + public AcqAddnDataGoodsInfo getAcq_addn_data_goods_info() { + return acq_addn_data_goods_info; + } + + public void setAcq_addn_data_goods_info(AcqAddnDataGoodsInfo acq_addn_data_goods_info) { + this.acq_addn_data_goods_info = acq_addn_data_goods_info; + } + + public String getFront_url() { + return front_url; + } + + public void setFront_url(String front_url) { + this.front_url = front_url; + } + + public String getFront_fail_url() { + return front_fail_url; + } + + public void setFront_fail_url(String front_fail_url) { + this.front_fail_url = front_fail_url; + } + + public String getUser_auth_code() { + return user_auth_code; + } + + public void setUser_auth_code(String user_auth_code) { + this.user_auth_code = user_auth_code; + } +} diff --git a/src/main/java/com/lakala/moss/api/params/AccRespFields.java b/src/main/java/com/lakala/moss/api/params/AccRespFields.java new file mode 100644 index 0000000..fb287b5 --- /dev/null +++ b/src/main/java/com/lakala/moss/api/params/AccRespFields.java @@ -0,0 +1,165 @@ +package com.lakala.moss.api.params; + +import com.google.gson.annotations.SerializedName; + +/** + * 请求参数-账户端返回信息域 + * + * @author Stephen yu + * @date 2024-12-17 + */ +public class AccRespFields { + // 商户可用此参数自定义去生成二维码后展示出来进行扫码支付;支付宝(41-NATIVE)场景下返回 + private String code; + // 商户收款二维码图片。Base64编码,暂无;支付宝(41-NATIVE)场景下返回 + private String code_image; + // 支付签名信息;微信(71-小程序)微信(51-JSAPI)场景下返回 + private String pay_sign; + // 小程序id;微信(71-小程序)微信(51-JSAPI)场景下返回 + private String app_id; + // 时间戳;微信(71-小程序)微信(51-JSAPI)场景下返回 + private String time_stamp; + // 随机字符串;微信(71-小程序)微信(51-JSAPI)场景下返回 + private String nonce_str; + // 订单详情扩展字符串;微信(71-小程序)微信(51-JSAPI)场景下返回 + @SerializedName("package") + private String spackage; + // 签名方式;微信(71-小程序)微信(51-JSAPI)场景下返回 + private String sign_type; + // 银联JS支付返回重定向地址;银联二维码(51-JSAPI)场景下返回 + private String redirect_url; + + // 订单查询特有 + // [支付宝]商户门店编号 + private String store_id; + // [支付宝]交易支付使用的资金渠道 + private String fund_bill_list; + // [微信]活动 ID,在账户端商户后台配置的批次 ID + private String acc_activity_id; + // [微信]优惠功能信息 + private String promotion_detail; + // [云闪付]银联单品营销 附加数据,参与单品营销优惠时返回 + private String up_iss_addn_data; + // [云闪付]银联优惠信息/出资方信息,参与单品营销优惠时返回 + private String up_coupon_info; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getCode_image() { + return code_image; + } + + public void setCode_image(String code_image) { + this.code_image = code_image; + } + + public String getPay_sign() { + return pay_sign; + } + + public void setPay_sign(String pay_sign) { + this.pay_sign = pay_sign; + } + + public String getApp_id() { + return app_id; + } + + public void setApp_id(String app_id) { + this.app_id = app_id; + } + + public String getTime_stamp() { + return time_stamp; + } + + public void setTime_stamp(String time_stamp) { + this.time_stamp = time_stamp; + } + + public String getNonce_str() { + return nonce_str; + } + + public void setNonce_str(String nonce_str) { + this.nonce_str = nonce_str; + } + + public String getSpackage() { + return spackage; + } + + public void setSpackage(String spackage) { + this.spackage = spackage; + } + + public String getSign_type() { + return sign_type; + } + + public void setSign_type(String sign_type) { + this.sign_type = sign_type; + } + + public String getRedirect_url() { + return redirect_url; + } + + public void setRedirect_url(String redirect_url) { + this.redirect_url = redirect_url; + } + + public String getStore_id() { + return store_id; + } + + public void setStore_id(String store_id) { + this.store_id = store_id; + } + + public String getFund_bill_list() { + return fund_bill_list; + } + + public void setFund_bill_list(String fund_bill_list) { + this.fund_bill_list = fund_bill_list; + } + + public String getAcc_activity_id() { + return acc_activity_id; + } + + public void setAcc_activity_id(String acc_activity_id) { + this.acc_activity_id = acc_activity_id; + } + + public String getPromotion_detail() { + return promotion_detail; + } + + public void setPromotion_detail(String promotion_detail) { + this.promotion_detail = promotion_detail; + } + + public String getUp_iss_addn_data() { + return up_iss_addn_data; + } + + public void setUp_iss_addn_data(String up_iss_addn_data) { + this.up_iss_addn_data = up_iss_addn_data; + } + + public String getUp_coupon_info() { + return up_coupon_info; + } + + public void setUp_coupon_info(String up_coupon_info) { + this.up_coupon_info = up_coupon_info; + } +} diff --git a/src/main/java/com/lakala/moss/api/params/AcqAddnDataGoodsInfo.java b/src/main/java/com/lakala/moss/api/params/AcqAddnDataGoodsInfo.java new file mode 100644 index 0000000..ab3ae46 --- /dev/null +++ b/src/main/java/com/lakala/moss/api/params/AcqAddnDataGoodsInfo.java @@ -0,0 +1,69 @@ +package com.lakala.moss.api.params; +/** + * 请求参数-银联单品营销商品信息 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class AcqAddnDataGoodsInfo { + // 商品编号 + private String id; + // 商品名称 + private String name; + // 商品单价 单位元 + private String price; + // 商品数量 + private String quantity; + // 商品类目 + private String category; + // 附加信息 + private String addninfo; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPrice() { + return price; + } + + public void setPrice(String price) { + this.price = price; + } + + public String getQuantity() { + return quantity; + } + + public void setQuantity(String quantity) { + this.quantity = quantity; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public String getAddninfo() { + return addninfo; + } + + public void setAddninfo(String addninfo) { + this.addninfo = addninfo; + } +} diff --git a/src/main/java/com/lakala/moss/api/params/AcqAddnDataOrderInfo.java b/src/main/java/com/lakala/moss/api/params/AcqAddnDataOrderInfo.java new file mode 100644 index 0000000..ac9ddc5 --- /dev/null +++ b/src/main/java/com/lakala/moss/api/params/AcqAddnDataOrderInfo.java @@ -0,0 +1,29 @@ +package com.lakala.moss.api.params; +/** + * 请求参数-银联单品营销订单信息 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class AcqAddnDataOrderInfo { + // 标题 + private String title; + // 金额 + private String dctAmount; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDctAmount() { + return dctAmount; + } + + public void setDctAmount(String dctAmount) { + this.dctAmount = dctAmount; + } +} diff --git a/src/main/java/com/lakala/moss/api/params/ExtendParams.java b/src/main/java/com/lakala/moss/api/params/ExtendParams.java new file mode 100644 index 0000000..cbd6edb --- /dev/null +++ b/src/main/java/com/lakala/moss/api/params/ExtendParams.java @@ -0,0 +1,29 @@ +package com.lakala.moss.api.params; +/** + * 请求参数-支付宝业务扩展参数 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class ExtendParams { + // 系统商编号,该参数作为系统商返佣数据提取的依据,请填写系统商签约协议的 PID + private String sys_service_provider_id; + // 点餐场景类型:qr_order(店内扫码点餐),pre_order(预点到店自提),home_delivery (外送到家),direct_payment(直接付款),other(其它) + private String food_order_type; + + public String getSys_service_provider_id() { + return sys_service_provider_id; + } + + public void setSys_service_provider_id(String sys_service_provider_id) { + this.sys_service_provider_id = sys_service_provider_id; + } + + public String getFood_order_type() { + return food_order_type; + } + + public void setFood_order_type(String food_order_type) { + this.food_order_type = food_order_type; + } +} diff --git a/src/main/java/com/lakala/moss/api/params/GoodsDetail.java b/src/main/java/com/lakala/moss/api/params/GoodsDetail.java new file mode 100644 index 0000000..9a1d566 --- /dev/null +++ b/src/main/java/com/lakala/moss/api/params/GoodsDetail.java @@ -0,0 +1,109 @@ +package com.lakala.moss.api.params; +/** + * 请求参数-商品详情 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class GoodsDetail { + // 商品的编号 + private String goods_id; + // 支付宝定义的统一商品编号 + private String alipay_goods_id; + // 商品名称 + private String goods_name; + // 商品数量 + private String quantity; + // 商品价格 单位元 + private String price; + // 商品类目 + private String goods_category; + // 商品类目树,从商品类目根节点到叶子节点的类目 id 组成,类目 id 值使用|分割 + private String categories_tree; + // 商品描述信息 + private String body; + // 商品的展示地址 + private String show_url; + // 微信支付定义的统一商品编号 + private String wxpay_goods_id; + + public String getGoods_id() { + return goods_id; + } + + public void setGoods_id(String goods_id) { + this.goods_id = goods_id; + } + + public String getAlipay_goods_id() { + return alipay_goods_id; + } + + public void setAlipay_goods_id(String alipay_goods_id) { + this.alipay_goods_id = alipay_goods_id; + } + + public String getGoods_name() { + return goods_name; + } + + public void setGoods_name(String goods_name) { + this.goods_name = goods_name; + } + + public String getQuantity() { + return quantity; + } + + public void setQuantity(String quantity) { + this.quantity = quantity; + } + + public String getPrice() { + return price; + } + + public void setPrice(String price) { + this.price = price; + } + + public String getGoods_category() { + return goods_category; + } + + public void setGoods_category(String goods_category) { + this.goods_category = goods_category; + } + + public String getCategories_tree() { + return categories_tree; + } + + public void setCategories_tree(String categories_tree) { + this.categories_tree = categories_tree; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public String getShow_url() { + return show_url; + } + + public void setShow_url(String show_url) { + this.show_url = show_url; + } + + public String getWxpay_goods_id() { + return wxpay_goods_id; + } + + public void setWxpay_goods_id(String wxpay_goods_id) { + this.wxpay_goods_id = wxpay_goods_id; + } +} diff --git a/src/main/java/com/lakala/moss/api/params/LocationInfo.java b/src/main/java/com/lakala/moss/api/params/LocationInfo.java new file mode 100644 index 0000000..ab9d2f2 --- /dev/null +++ b/src/main/java/com/lakala/moss/api/params/LocationInfo.java @@ -0,0 +1,42 @@ +package com.lakala.moss.api.params; + +/** + * 请求参数-地址位置信息 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class LocationInfo { + // 请求方的IP地址,存在必填,格式如36.45.36.95 + private String request_ip; + // 客户端设备的基站信息 + private String base_station; + // 商户终端的地理位置,整体格式:纬度,经度,+表示北纬、东经,-表示南纬、 西经。 + // 经度格式:1位正负号+3位整数+1位小数点+5位小数; 纬度格式:1位正负号+2位整数+1位小数点+6位小数; + // 举例:+31.221345,+121.12345 + private String location; + + public String getRequest_ip() { + return request_ip; + } + + public void setRequest_ip(String request_ip) { + this.request_ip = request_ip; + } + + public String getBase_station() { + return base_station; + } + + public void setBase_station(String base_station) { + this.base_station = base_station; + } + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } +} diff --git a/src/main/java/com/lakala/moss/api/params/PayInfo.java b/src/main/java/com/lakala/moss/api/params/PayInfo.java new file mode 100644 index 0000000..4a51ae2 --- /dev/null +++ b/src/main/java/com/lakala/moss/api/params/PayInfo.java @@ -0,0 +1,210 @@ +package com.lakala.moss.api.params; + +/** + * 请求参数-支付信息 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class PayInfo { + // 支付流水号,仅交易类型为PAY-支付时存在。 + private String pay_serial; + // 对账单流水号,根据交易类型不同,或为支付对账单流水号 或为退款对账单流水号 + private String log_no; + // 账户端交易订单号,根据交易类型不同,或为支付账户端交易订单号 或为退款账户端交易订单号 + private String acc_trade_no; + // 支付交易完成时间,或退款交易完成时间。格式yyyyMMddHHmmss + private String trade_time; + // 原支付流水号,trade_main_type=REFUND时存在该字段 + private String origin_pay_serial; + // 申请退款订单金额,单位分,trade_main_type=REFUND时存在该字段 + private String refund_amount; + // 钱包类型;微信:WECHAT 支付宝:ALIPAY 银联云闪付:UQRCODEPAY + private String account_type; + // 交易状态 + //INIT-初始化 + //CREATE-待支付 + //SUCCESS-交易成功 + //FAIL-交易失败 + //DEAL-交易处理中 + //CLOSE-订单关闭 + //PART_REFUND-部分退款 + //REFUND-全部退款 + //EXPIRE-已过期 + //UNKNOWN-未知状态 + private String trade_state; + // 付款银行 + private String bank_type; + // 银行卡类型,00:借记 01:贷记 02:微信零钱 03:支付宝花呗 04:支付宝其他 05:数字货币 06:拉卡拉支付账户 99:未知 + private String card_type; + // 付款人实付金额,单位分 + private String payer_amount; + // 账户端应结订单金额,单位分 + private String acc_settle_amount; + // 商户优惠金额,单位分 + private String acc_mdiscount_amount; + // 账户端优惠金额,单位分 + private String acc_discount_amount; + // 账户端其它优惠金额,单位分 + private String acc_other_discount_amount; + // 备注信息 + private String remark; + // 用户标识1,微信sub_open_id 支付宝buyer_logon_id(买家支付宝账号) + private String user_id1; + // 用户标识2,微信openId支付宝buyer_user_id 银联user_id + private String user_id2; + // 账户端子商户号 + private String sub_mch_id; + + public String getPay_serial() { + return pay_serial; + } + + public void setPay_serial(String pay_serial) { + this.pay_serial = pay_serial; + } + + public String getLog_no() { + return log_no; + } + + public void setLog_no(String log_no) { + this.log_no = log_no; + } + + public String getAcc_trade_no() { + return acc_trade_no; + } + + public void setAcc_trade_no(String acc_trade_no) { + this.acc_trade_no = acc_trade_no; + } + + public String getTrade_time() { + return trade_time; + } + + public void setTrade_time(String trade_time) { + this.trade_time = trade_time; + } + + public String getOrigin_pay_serial() { + return origin_pay_serial; + } + + public void setOrigin_pay_serial(String origin_pay_serial) { + this.origin_pay_serial = origin_pay_serial; + } + + public String getRefund_amount() { + return refund_amount; + } + + public void setRefund_amount(String refund_amount) { + this.refund_amount = refund_amount; + } + + public String getAccount_type() { + return account_type; + } + + public void setAccount_type(String account_type) { + this.account_type = account_type; + } + + public String getTrade_state() { + return trade_state; + } + + public void setTrade_state(String trade_state) { + this.trade_state = trade_state; + } + + public String getBank_type() { + return bank_type; + } + + public void setBank_type(String bank_type) { + this.bank_type = bank_type; + } + + public String getCard_type() { + return card_type; + } + + public void setCard_type(String card_type) { + this.card_type = card_type; + } + + public String getPayer_amount() { + return payer_amount; + } + + public void setPayer_amount(String payer_amount) { + this.payer_amount = payer_amount; + } + + public String getAcc_settle_amount() { + return acc_settle_amount; + } + + public void setAcc_settle_amount(String acc_settle_amount) { + this.acc_settle_amount = acc_settle_amount; + } + + public String getAcc_mdiscount_amount() { + return acc_mdiscount_amount; + } + + public void setAcc_mdiscount_amount(String acc_mdiscount_amount) { + this.acc_mdiscount_amount = acc_mdiscount_amount; + } + + public String getAcc_discount_amount() { + return acc_discount_amount; + } + + public void setAcc_discount_amount(String acc_discount_amount) { + this.acc_discount_amount = acc_discount_amount; + } + + public String getAcc_other_discount_amount() { + return acc_other_discount_amount; + } + + public void setAcc_other_discount_amount(String acc_other_discount_amount) { + this.acc_other_discount_amount = acc_other_discount_amount; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public String getUser_id1() { + return user_id1; + } + + public void setUser_id1(String user_id1) { + this.user_id1 = user_id1; + } + + public String getUser_id2() { + return user_id2; + } + + public void setUser_id2(String user_id2) { + this.user_id2 = user_id2; + } + + public String getSub_mch_id() { + return sub_mch_id; + } + + public void setSub_mch_id(String sub_mch_id) { + this.sub_mch_id = sub_mch_id; + } +} diff --git a/src/main/java/com/lakala/moss/api/params/SplitInfo.java b/src/main/java/com/lakala/moss/api/params/SplitInfo.java new file mode 100644 index 0000000..47d570a --- /dev/null +++ b/src/main/java/com/lakala/moss/api/params/SplitInfo.java @@ -0,0 +1,81 @@ +package com.lakala.moss.api.params; + +/** + * 请求参数-拆单信息 + * + * @author Stephen yu + * @date 2025-04-11 + */ +public class SplitInfo { + // 子单支付流水号 + private String sub_pay_serial; + // 商户号 + private String mer_no; + // 金额,单位分 + private String amount; + // 子单对账单流水号 + private String sub_log_no; + // 以下为合单退款特有字段 + // 申请退款金额,单位分 + private String refund_amount; + // 原子单支付流水号 + private String origin_sub_pay_serial; + // 仅交易类型为PREFUND-退款时存在。表示子单退货状态 SUCCESS-交易成功 + private String sub_trade_state; + + public String getSub_pay_serial() { + return sub_pay_serial; + } + + public void setSub_pay_serial(String sub_pay_serial) { + this.sub_pay_serial = sub_pay_serial; + } + + public String getMer_no() { + return mer_no; + } + + public void setMer_no(String mer_no) { + this.mer_no = mer_no; + } + + public String getAmount() { + return amount; + } + + public void setAmount(String amount) { + this.amount = amount; + } + + public String getSub_log_no() { + return sub_log_no; + } + + public void setSub_log_no(String sub_log_no) { + this.sub_log_no = sub_log_no; + } + + public String getRefund_amount() { + return refund_amount; + } + + public void setRefund_amount(String refund_amount) { + this.refund_amount = refund_amount; + } + + public String getOrigin_sub_pay_serial() { + return origin_sub_pay_serial; + } + + public void setOrigin_sub_pay_serial(String origin_sub_pay_serial) { + this.origin_sub_pay_serial = origin_sub_pay_serial; + } + + public String getSub_trade_state() { + return sub_trade_state; + } + + public void setSub_trade_state(String sub_trade_state) { + this.sub_trade_state = sub_trade_state; + } +} diff --git a/src/main/java/com/lakala/moss/api/request/OrderChkApplyReq.java b/src/main/java/com/lakala/moss/api/request/OrderChkApplyReq.java new file mode 100644 index 0000000..41b3dbb --- /dev/null +++ b/src/main/java/com/lakala/moss/api/request/OrderChkApplyReq.java @@ -0,0 +1,31 @@ +package com.lakala.moss.api.request; + +/** + * 请求参数-申请对账单 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class OrderChkApplyReq { + + // 账单日期,格式:yyyyMMdd,仅支持三个月内的账单下载申请 + private String tran_date; + // 申请订单号 + private String apply_order_no; + + public String getTran_date() { + return tran_date; + } + + public void setTran_date(String tran_date) { + this.tran_date = tran_date; + } + + public String getApply_order_no() { + return apply_order_no; + } + + public void setApply_order_no(String apply_order_no) { + this.apply_order_no = apply_order_no; + } +} diff --git a/src/main/java/com/lakala/moss/api/request/OrderClsReq.java b/src/main/java/com/lakala/moss/api/request/OrderClsReq.java new file mode 100644 index 0000000..f0f12dd --- /dev/null +++ b/src/main/java/com/lakala/moss/api/request/OrderClsReq.java @@ -0,0 +1,34 @@ +package com.lakala.moss.api.request; + +import com.lakala.moss.api.params.LocationInfo; + +/** + * 请求参数-关单交易 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class OrderClsReq { + + // 原商户支付订单号 + private String origin_order_no; + // 地址位置信息,风控要求必送 + private LocationInfo location_info; + + public String getOrigin_order_no() { + return origin_order_no; + } + + public void setOrigin_order_no(String origin_order_no) { + this.origin_order_no = origin_order_no; + } + + public LocationInfo getLocation_info() { + return location_info; + } + + public void setLocation_info(LocationInfo location_info) { + this.location_info = location_info; + } + +} diff --git a/src/main/java/com/lakala/moss/api/request/OrderNoticeReq.java b/src/main/java/com/lakala/moss/api/request/OrderNoticeReq.java new file mode 100644 index 0000000..cf0a3e4 --- /dev/null +++ b/src/main/java/com/lakala/moss/api/request/OrderNoticeReq.java @@ -0,0 +1,304 @@ +package com.lakala.moss.api.request; + +/** + * 请求参数-订单通知 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class OrderNoticeReq { + + // 交易类型。PAY-支付,REFUND-退款 + private String trade_main_type; + // 商户订单号,根据交易类型不同,或为支付订单号 或为退款订单号 + private String order_no; + // 商户号 + private String mer_no; + // 订单金额,单位分 + private String total_amount; + // 订单标题,用于简单描述订单或商品主题。最多42个字符,如不送,默认显示商户简称 + private String subject; + // 订单创建时间,格式yyyyMMddHHmmss + private String order_create_time; + // 订单有效时间,分钟 + private String order_eff_time; + // 订单层状态: + // SUCCESS-已支付 + // PART_REFUND-部分退款 (当商户订单号下存在未全额退款成功的支付流水号时) + // REFUND-全部退款(当商户订单号下资金全部退完,为全部退款) + private String order_status; + // 支付流水号,仅交易类型为PAY-支付时存在。 + private String pay_serial; + // 对账单流水号,根据交易类型不同,或为支付对账单流水号 或为退款对账单流水号 + private String log_no; + // 账户端交易订单号,根据交易类型不同,或为支付账户端交易订单号 或为退款账户端交易订单号 + private String acc_trade_no; + // 支付交易完成时间,或退款交易完成时间。格式yyyyMMddHHmmss + private String trade_time; + // 原商户支付订单号,trade_main_type=REFUND时存在该字段 + private String origin_order_no; + // 原支付流水号,trade_main_type=REFUND时存在该字段 + private String origin_pay_serial; + // 申请退款订单金额,单位分,trade_main_type=REFUND时存在该字段 + private String refund_amount; + // 钱包类型;微信:WECHAT 支付宝:ALIPAY 银联云闪付:UQRCODEPAY + private String account_type; + // 接入方式:41:NATIVE((ALIPAY,云闪付支持)51:JSAPI(微信公众号支付,支付宝服务窗支付,银联JS支付)71:微信小程序支付 + private String trans_type; + // 交易状态:SUCCESS-交易成功 + private String trade_state; + // 付款银行 + private String bank_type; + // 银行卡类型,00:借记 01:贷记 02:微信零钱 03:支付宝花呗 04:支付宝其他 05:数字货币 06:拉卡拉支付账户 99:未知 + private String card_type; + // 付款人实付金额,单位分 + private String payer_amount; + // 账户端应结订单金额,单位分 + private String acc_settle_amount; + // 商户优惠金额,单位分 + private String acc_mdiscount_amount; + // 账户端优惠金额,单位分 + private String acc_discount_amount; + // 账户端其它优惠金额,单位分 + private String acc_other_discount_amount; + // 备注信息 + private String remark; + // 用户标识1,微信sub_open_id 支付宝buyer_logon_id(买家支付宝账号) + private String user_id1; + // 用户标识2,微信openId支付宝buyer_user_id 银联user_id + private String user_id2; + // 账户端子商户号 + private String sub_mch_id; + + public String getTrade_main_type() { + return trade_main_type; + } + + public void setTrade_main_type(String trade_main_type) { + this.trade_main_type = trade_main_type; + } + + public String getOrder_no() { + return order_no; + } + + public void setOrder_no(String order_no) { + this.order_no = order_no; + } + + public String getMer_no() { + return mer_no; + } + + public void setMer_no(String mer_no) { + this.mer_no = mer_no; + } + + public String getTotal_amount() { + return total_amount; + } + + public void setTotal_amount(String total_amount) { + this.total_amount = total_amount; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getOrder_create_time() { + return order_create_time; + } + + public void setOrder_create_time(String order_create_time) { + this.order_create_time = order_create_time; + } + + public String getOrder_eff_time() { + return order_eff_time; + } + + public void setOrder_eff_time(String order_eff_time) { + this.order_eff_time = order_eff_time; + } + + public String getOrder_status() { + return order_status; + } + + public void setOrder_status(String order_status) { + this.order_status = order_status; + } + + public String getPay_serial() { + return pay_serial; + } + + public void setPay_serial(String pay_serial) { + this.pay_serial = pay_serial; + } + + public String getLog_no() { + return log_no; + } + + public void setLog_no(String log_no) { + this.log_no = log_no; + } + + public String getAcc_trade_no() { + return acc_trade_no; + } + + public void setAcc_trade_no(String acc_trade_no) { + this.acc_trade_no = acc_trade_no; + } + + public String getTrade_time() { + return trade_time; + } + + public void setTrade_time(String trade_time) { + this.trade_time = trade_time; + } + + public String getOrigin_order_no() { + return origin_order_no; + } + + public void setOrigin_order_no(String origin_order_no) { + this.origin_order_no = origin_order_no; + } + + public String getOrigin_pay_serial() { + return origin_pay_serial; + } + + public void setOrigin_pay_serial(String origin_pay_serial) { + this.origin_pay_serial = origin_pay_serial; + } + + public String getRefund_amount() { + return refund_amount; + } + + public void setRefund_amount(String refund_amount) { + this.refund_amount = refund_amount; + } + + public String getAccount_type() { + return account_type; + } + + public void setAccount_type(String account_type) { + this.account_type = account_type; + } + + public String getTrans_type() { + return trans_type; + } + + public void setTrans_type(String trans_type) { + this.trans_type = trans_type; + } + + public String getTrade_state() { + return trade_state; + } + + public void setTrade_state(String trade_state) { + this.trade_state = trade_state; + } + + public String getBank_type() { + return bank_type; + } + + public void setBank_type(String bank_type) { + this.bank_type = bank_type; + } + + public String getCard_type() { + return card_type; + } + + public void setCard_type(String card_type) { + this.card_type = card_type; + } + + public String getPayer_amount() { + return payer_amount; + } + + public void setPayer_amount(String payer_amount) { + this.payer_amount = payer_amount; + } + + public String getAcc_settle_amount() { + return acc_settle_amount; + } + + public void setAcc_settle_amount(String acc_settle_amount) { + this.acc_settle_amount = acc_settle_amount; + } + + public String getAcc_mdiscount_amount() { + return acc_mdiscount_amount; + } + + public void setAcc_mdiscount_amount(String acc_mdiscount_amount) { + this.acc_mdiscount_amount = acc_mdiscount_amount; + } + + public String getAcc_discount_amount() { + return acc_discount_amount; + } + + public void setAcc_discount_amount(String acc_discount_amount) { + this.acc_discount_amount = acc_discount_amount; + } + + public String getAcc_other_discount_amount() { + return acc_other_discount_amount; + } + + public void setAcc_other_discount_amount(String acc_other_discount_amount) { + this.acc_other_discount_amount = acc_other_discount_amount; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public String getUser_id1() { + return user_id1; + } + + public void setUser_id1(String user_id1) { + this.user_id1 = user_id1; + } + + public String getUser_id2() { + return user_id2; + } + + public void setUser_id2(String user_id2) { + this.user_id2 = user_id2; + } + + public String getSub_mch_id() { + return sub_mch_id; + } + + public void setSub_mch_id(String sub_mch_id) { + this.sub_mch_id = sub_mch_id; + } +} diff --git a/src/main/java/com/lakala/moss/api/request/OrderPayReq.java b/src/main/java/com/lakala/moss/api/request/OrderPayReq.java new file mode 100644 index 0000000..f3b86ec --- /dev/null +++ b/src/main/java/com/lakala/moss/api/request/OrderPayReq.java @@ -0,0 +1,162 @@ +package com.lakala.moss.api.request; + +import com.lakala.moss.api.params.AccBusiFields; +import com.lakala.moss.api.params.LocationInfo; + +/** + * 请求参数-订单支付 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class OrderPayReq { + + // 商户订单号,32位,全局唯一,建议规则:businessChannel+日期时间+序列流水号 + private String order_no; + // 订单总金额,分为单位,整数。示例:1元应填写 100 + private String total_amount; + // 商户号,平台客户下所发展的商户,通过MOSS平台新增商户产生,M+8位数字; + private String mer_no; + // 订单备注信息 + private String remark; + // 后端支付结果通知url,支付结果通知的平台后台回调地址,通知url必须为外网可访问的url,不能携带参数 + private String notify_url; + // 支付场景 0-标准收银台 1-独立支付API + private String pay_scene; + // 钱包类型: + //pay_scene=0为非必传,可多传,表示为指定收银台内展示的支付方式;当pay_scene=1模式时必传单个支付方式。 + //ALIPAY:支付宝 + //WECHAT:微信 + //UQRCODEPAY:银联云闪付 + private String account_type; + // 订单标题,用于简单描述订单或商品主题。最多42个字符,如不送,默认显示商户简称 + private String subject; + // 订单有效期,格式yyyyMMddHHmmss,建议不超过15分钟。不传值则默认5分钟。 + private String order_eff_time; + // 当客户微信公众号/支付宝生活号/云闪付H5支付场景时,拉起MOSS-H5收银台支付成功后,跳转至客户指定的URL页面地址; + private String callback_url; + // 接入方式: + //41:NATIVE((ALIPAY,云闪付支持) + //51:JSAPI(微信公众号支付,支付宝服务窗支付,银联JS支付) + //71:微信小程序支付 + private String trans_type; + // 地址位置信息,风控要求必送 + private LocationInfo location_info; + // 用户在钱包侧唯一标识,微信支付宝侧为openid,云闪付为userid。trans_type为51情况下传入。 + private String user_id; + // 账户端业务信息域,不同类型的account_type对应不同的账户端,需要填写以下不同的信息 + private AccBusiFields acc_busi_fields; + + public String getOrder_no() { + return order_no; + } + + public void setOrder_no(String order_no) { + this.order_no = order_no; + } + + public String getTotal_amount() { + return total_amount; + } + + public void setTotal_amount(String total_amount) { + this.total_amount = total_amount; + } + + public String getMer_no() { + return mer_no; + } + + public void setMer_no(String mer_no) { + this.mer_no = mer_no; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public String getNotify_url() { + return notify_url; + } + + public void setNotify_url(String notify_url) { + this.notify_url = notify_url; + } + + public String getPay_scene() { + return pay_scene; + } + + public void setPay_scene(String pay_scene) { + this.pay_scene = pay_scene; + } + + public String getAccount_type() { + return account_type; + } + + public void setAccount_type(String account_type) { + this.account_type = account_type; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getOrder_eff_time() { + return order_eff_time; + } + + public void setOrder_eff_time(String order_eff_time) { + this.order_eff_time = order_eff_time; + } + + public String getCallback_url() { + return callback_url; + } + + public void setCallback_url(String callback_url) { + this.callback_url = callback_url; + } + + public String getTrans_type() { + return trans_type; + } + + public void setTrans_type(String trans_type) { + this.trans_type = trans_type; + } + + public LocationInfo getLocation_info() { + return location_info; + } + + public void setLocation_info(LocationInfo location_info) { + this.location_info = location_info; + } + + public String getUser_id() { + return user_id; + } + + public void setUser_id(String user_id) { + this.user_id = user_id; + } + + public AccBusiFields getAcc_busi_fields() { + return acc_busi_fields; + } + + public void setAcc_busi_fields(AccBusiFields acc_busi_fields) { + this.acc_busi_fields = acc_busi_fields; + } + +} diff --git a/src/main/java/com/lakala/moss/api/request/OrderQryListReq.java b/src/main/java/com/lakala/moss/api/request/OrderQryListReq.java new file mode 100644 index 0000000..070af9c --- /dev/null +++ b/src/main/java/com/lakala/moss/api/request/OrderQryListReq.java @@ -0,0 +1,32 @@ +package com.lakala.moss.api.request; + +/** + * 请求参数-订单查询列表 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class OrderQryListReq { + + // 商户订单号/退款订单号,32位,全局唯一,建议规则:businessChannel+日期时间+序列流水号 + private String order_no; + // 支付流水号,上送则查账指定的支付流水号 + private String pay_serial; + + public String getOrder_no() { + return order_no; + } + + public void setOrder_no(String order_no) { + this.order_no = order_no; + } + + public String getPay_serial() { + return pay_serial; + } + + public void setPay_serial(String pay_serial) { + this.pay_serial = pay_serial; + } + +} diff --git a/src/main/java/com/lakala/moss/api/request/OrderRefundReq.java b/src/main/java/com/lakala/moss/api/request/OrderRefundReq.java new file mode 100644 index 0000000..04566f1 --- /dev/null +++ b/src/main/java/com/lakala/moss/api/request/OrderRefundReq.java @@ -0,0 +1,74 @@ +package com.lakala.moss.api.request; + +import com.lakala.moss.api.params.LocationInfo; + +/** + * 请求参数-退款交易 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class OrderRefundReq { + + // 退款订单号 + private String order_no; + // 原商户支付订单号 + private String origin_order_no; + // 原支付流水号(可指定需要退款的支付流水号) + private String origin_pay_serial; + // 退款金额,以分为单位 + private String refund_amount; + // 退款原因描述 + private String refund_reason; + // 地址位置信息,风控要求必送 + private LocationInfo location_info; + + public String getOrder_no() { + return order_no; + } + + public void setOrder_no(String order_no) { + this.order_no = order_no; + } + + public String getOrigin_order_no() { + return origin_order_no; + } + + public void setOrigin_order_no(String origin_order_no) { + this.origin_order_no = origin_order_no; + } + + public String getOrigin_pay_serial() { + return origin_pay_serial; + } + + public void setOrigin_pay_serial(String origin_pay_serial) { + this.origin_pay_serial = origin_pay_serial; + } + + public String getRefund_amount() { + return refund_amount; + } + + public void setRefund_amount(String refund_amount) { + this.refund_amount = refund_amount; + } + + public String getRefund_reason() { + return refund_reason; + } + + public void setRefund_reason(String refund_reason) { + this.refund_reason = refund_reason; + } + + public LocationInfo getLocation_info() { + return location_info; + } + + public void setLocation_info(LocationInfo location_info) { + this.location_info = location_info; + } + +} diff --git a/src/main/java/com/lakala/moss/api/request/UseridQryReq.java b/src/main/java/com/lakala/moss/api/request/UseridQryReq.java new file mode 100644 index 0000000..95cfab1 --- /dev/null +++ b/src/main/java/com/lakala/moss/api/request/UseridQryReq.java @@ -0,0 +1,43 @@ +package com.lakala.moss.api.request; + +/** + * 请求参数-用户标识查询 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class UseridQryReq { + + // 商户号,平台客户下所发展的商户,通过MOSS平台新增商户产生,M+8位数字; + private String mer_no; + // 付款方返回的临时授权码,一次有效。 + //银联云闪付APP访问收单商户H5页面,收款方返回302重定向地址。 + //具体格式如下: “ https://qr.95516.com/qrcGtwWeb-web/api/userAuth?version=1.0.0&redirectUrl=收单商户接收处理结果的地址,需进行URLEncode”,成功后返回临时授权码 + private String auth_code; + // 银联支付标识.收款方识别HTTP请求User Agent中包含银联支付标识,格式为“UnionPay/<版本号> ”,注意APP标识仅支持字母和数字,例:UnionPay/1.0 ICBCeLife + private String app_up_identifier; + + public String getMer_no() { + return mer_no; + } + + public void setMer_no(String mer_no) { + this.mer_no = mer_no; + } + + public String getAuth_code() { + return auth_code; + } + + public void setAuth_code(String auth_code) { + this.auth_code = auth_code; + } + + public String getApp_up_identifier() { + return app_up_identifier; + } + + public void setApp_up_identifier(String app_up_identifier) { + this.app_up_identifier = app_up_identifier; + } +} diff --git a/src/main/java/com/lakala/moss/api/response/OrderChkApplyRes.java b/src/main/java/com/lakala/moss/api/response/OrderChkApplyRes.java new file mode 100644 index 0000000..767f032 --- /dev/null +++ b/src/main/java/com/lakala/moss/api/response/OrderChkApplyRes.java @@ -0,0 +1,31 @@ +package com.lakala.moss.api.response; + +/** + * 响应参数-申请对账单 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class OrderChkApplyRes { + + // 文件下载地址,供下一步请求账单文件的下载地址,该地址5min内有效。 + private String down_load_url; + // 申请订单号 + private String apply_order_no; + + public String getDown_load_url() { + return down_load_url; + } + + public void setDown_load_url(String down_load_url) { + this.down_load_url = down_load_url; + } + + public String getApply_order_no() { + return apply_order_no; + } + + public void setApply_order_no(String apply_order_no) { + this.apply_order_no = apply_order_no; + } +} diff --git a/src/main/java/com/lakala/moss/api/response/OrderClsRes.java b/src/main/java/com/lakala/moss/api/response/OrderClsRes.java new file mode 100644 index 0000000..e1cf1cf --- /dev/null +++ b/src/main/java/com/lakala/moss/api/response/OrderClsRes.java @@ -0,0 +1,32 @@ +package com.lakala.moss.api.response; + +/** + * 响应参数-关单交易 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class OrderClsRes { + + // 原商户支付订单号 + private String origin_order_no; + // 交易时间,格式yyyyMMddHHmmss + private String trade_time; + + public String getOrigin_order_no() { + return origin_order_no; + } + + public void setOrigin_order_no(String origin_order_no) { + this.origin_order_no = origin_order_no; + } + + public String getTrade_time() { + return trade_time; + } + + public void setTrade_time(String trade_time) { + this.trade_time = trade_time; + } + +} diff --git a/src/main/java/com/lakala/moss/api/response/OrderNoticeRes.java b/src/main/java/com/lakala/moss/api/response/OrderNoticeRes.java new file mode 100644 index 0000000..907e941 --- /dev/null +++ b/src/main/java/com/lakala/moss/api/response/OrderNoticeRes.java @@ -0,0 +1,13 @@ +package com.lakala.moss.api.response; + +/** + * 响应参数-订单通知 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class OrderNoticeRes { + + + +} diff --git a/src/main/java/com/lakala/moss/api/response/OrderPayRes.java b/src/main/java/com/lakala/moss/api/response/OrderPayRes.java new file mode 100644 index 0000000..03d5354 --- /dev/null +++ b/src/main/java/com/lakala/moss/api/response/OrderPayRes.java @@ -0,0 +1,103 @@ +package com.lakala.moss.api.response; + +import com.lakala.moss.api.params.AccRespFields; + +/** + * 响应参数-订单支付 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class OrderPayRes { + + // 商户订单号 + private String order_no; + // 支付流水号 + private String pay_serial; + // 账户端的商户单号 + private String trade_no; + // 对账单流水号 + private String log_no; + // 支付场景 0-拉起收银台 1-独立支付API + private String pay_scene; + // 账户端子商户号 + private String sub_mch_id; + // [微信小程序/微信公众号/支付宝生活号]支付时返回,预下单Id,JSAPI支付方式时返回 + private String prepay_id; + // 账户端返回信息域 + private AccRespFields acc_resp_fields; + // 收银台地址url,可嵌入至H5、PC或集成小程序场景中 + private String counter_url; + + public String getOrder_no() { + return order_no; + } + + public void setOrder_no(String order_no) { + this.order_no = order_no; + } + + public String getPay_serial() { + return pay_serial; + } + + public void setPay_serial(String pay_serial) { + this.pay_serial = pay_serial; + } + + public String getTrade_no() { + return trade_no; + } + + public void setTrade_no(String trade_no) { + this.trade_no = trade_no; + } + + public String getLog_no() { + return log_no; + } + + public void setLog_no(String log_no) { + this.log_no = log_no; + } + + public String getPay_scene() { + return pay_scene; + } + + public void setPay_scene(String pay_scene) { + this.pay_scene = pay_scene; + } + + public String getSub_mch_id() { + return sub_mch_id; + } + + public void setSub_mch_id(String sub_mch_id) { + this.sub_mch_id = sub_mch_id; + } + + public String getPrepay_id() { + return prepay_id; + } + + public void setPrepay_id(String prepay_id) { + this.prepay_id = prepay_id; + } + + public AccRespFields getAcc_resp_fields() { + return acc_resp_fields; + } + + public void setAcc_resp_fields(AccRespFields acc_resp_fields) { + this.acc_resp_fields = acc_resp_fields; + } + + public String getCounter_url() { + return counter_url; + } + + public void setCounter_url(String counter_url) { + this.counter_url = counter_url; + } +} diff --git a/src/main/java/com/lakala/moss/api/response/OrderQryListRes.java b/src/main/java/com/lakala/moss/api/response/OrderQryListRes.java new file mode 100644 index 0000000..7d1549d --- /dev/null +++ b/src/main/java/com/lakala/moss/api/response/OrderQryListRes.java @@ -0,0 +1,130 @@ +package com.lakala.moss.api.response; + +import com.lakala.moss.api.params.PayInfo; +import java.util.List; + +/** + * 响应参数-订单查询列表 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class OrderQryListRes { + + // 交易类型。PAY-支付,REFUND-退款 + private String trade_main_type; + // 商户订单号,根据交易类型不同,或为支付订单号 或为退款订单号 + private String order_no; + // 原商户支付订单号,trade_main_type=REFUND时存在该字段 + private String origin_order_no; + // 商户号 + private String mer_no; + // 订单金额,单位分 + private String total_amount; + // 订单标题,用于简单描述订单或商品主题。最多42个字符,如不送,默认显示商户简称 + private String subject; + // 订单创建时间,格式yyyyMMddHHmmss + private String order_create_time; + // 订单有效时间,分钟 + private String order_eff_time; + // 订单层状态: + //INIT-初始化 + //CREATE-待支付 + //SUCCESS-已支付 + //CLOSE-订单关闭 + //PART_REFUND-部分退款 (当商户订单号下存在未全额退款成功的支付流水号时) + //REFUND-全部退款(当商户订单号下资金全部退完,为全部退款) + private String order_status; + // 接入方式:41:NATIVE((ALIPAY,云闪付支持)51:JSAPI(微信公众号支付,支付宝服务窗支付,银联JS支付)71:微信小程序支付 + private String trans_type; + // 支付信息列表 + private List pay_info_list; + + public String getTrade_main_type() { + return trade_main_type; + } + + public void setTrade_main_type(String trade_main_type) { + this.trade_main_type = trade_main_type; + } + + public String getOrder_no() { + return order_no; + } + + public void setOrder_no(String order_no) { + this.order_no = order_no; + } + + public String getOrigin_order_no() { + return origin_order_no; + } + + public void setOrigin_order_no(String origin_order_no) { + this.origin_order_no = origin_order_no; + } + + public String getMer_no() { + return mer_no; + } + + public void setMer_no(String mer_no) { + this.mer_no = mer_no; + } + + public String getTotal_amount() { + return total_amount; + } + + public void setTotal_amount(String total_amount) { + this.total_amount = total_amount; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getOrder_create_time() { + return order_create_time; + } + + public void setOrder_create_time(String order_create_time) { + this.order_create_time = order_create_time; + } + + public String getOrder_eff_time() { + return order_eff_time; + } + + public void setOrder_eff_time(String order_eff_time) { + this.order_eff_time = order_eff_time; + } + + public String getOrder_status() { + return order_status; + } + + public void setOrder_status(String order_status) { + this.order_status = order_status; + } + + public String getTrans_type() { + return trans_type; + } + + public void setTrans_type(String trans_type) { + this.trans_type = trans_type; + } + + public List getPay_info_list() { + return pay_info_list; + } + + public void setPay_info_list(List pay_info_list) { + this.pay_info_list = pay_info_list; + } +} diff --git a/src/main/java/com/lakala/moss/api/response/OrderRefundRes.java b/src/main/java/com/lakala/moss/api/response/OrderRefundRes.java new file mode 100644 index 0000000..6a8aac6 --- /dev/null +++ b/src/main/java/com/lakala/moss/api/response/OrderRefundRes.java @@ -0,0 +1,127 @@ +package com.lakala.moss.api.response; + +/** + * 响应参数-退款交易 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class OrderRefundRes { + + // 商户退款订单号 + private String order_no; + // 原商户支付订单号 + private String origin_order_no; + // 原支付流水号 + private String origin_pay_serial; + // 对账单流水号 + private String log_no; + // 账户端交易订单号 + private String acc_trade_no; + // 商户号 + private String mer_no; + // 交易金额,单位分 + private String total_amount; + // 申请退款金额,单位分 + private String refund_amount; + // 实际退款金额,单位分 + private String payer_amount; + // 退款时间,yyyyMMddHHmmss + private String trade_time; + // 交易状态 + //INIT-初始化; + //SUCCESS-交易成功; + //FAIL-交易失败; + //DEAL-交易处理中/未知; + //TIMEOUT-超时未知; + //EXCEPTION-异常; + private String trade_state; + + public String getOrder_no() { + return order_no; + } + + public void setOrder_no(String order_no) { + this.order_no = order_no; + } + + public String getOrigin_order_no() { + return origin_order_no; + } + + public void setOrigin_order_no(String origin_order_no) { + this.origin_order_no = origin_order_no; + } + + public String getOrigin_pay_serial() { + return origin_pay_serial; + } + + public void setOrigin_pay_serial(String origin_pay_serial) { + this.origin_pay_serial = origin_pay_serial; + } + + public String getLog_no() { + return log_no; + } + + public void setLog_no(String log_no) { + this.log_no = log_no; + } + + public String getAcc_trade_no() { + return acc_trade_no; + } + + public void setAcc_trade_no(String acc_trade_no) { + this.acc_trade_no = acc_trade_no; + } + + public String getMer_no() { + return mer_no; + } + + public void setMer_no(String mer_no) { + this.mer_no = mer_no; + } + + public String getTotal_amount() { + return total_amount; + } + + public void setTotal_amount(String total_amount) { + this.total_amount = total_amount; + } + + public String getRefund_amount() { + return refund_amount; + } + + public void setRefund_amount(String refund_amount) { + this.refund_amount = refund_amount; + } + + public String getPayer_amount() { + return payer_amount; + } + + public void setPayer_amount(String payer_amount) { + this.payer_amount = payer_amount; + } + + public String getTrade_time() { + return trade_time; + } + + public void setTrade_time(String trade_time) { + this.trade_time = trade_time; + } + + public String getTrade_state() { + return trade_state; + } + + public void setTrade_state(String trade_state) { + this.trade_state = trade_state; + } +} diff --git a/src/main/java/com/lakala/moss/api/response/UseridQryRes.java b/src/main/java/com/lakala/moss/api/response/UseridQryRes.java new file mode 100644 index 0000000..fe49836 --- /dev/null +++ b/src/main/java/com/lakala/moss/api/response/UseridQryRes.java @@ -0,0 +1,21 @@ +package com.lakala.moss.api.response; + +/** + * 响应参数-用户标识查询 + * + * @author Stephen yu + * @date 2025-02-21 + */ +public class UseridQryRes { + + // 银联钱包的userId + private String user_id; + + public String getUser_id() { + return user_id; + } + + public void setUser_id(String user_id) { + this.user_id = user_id; + } +} diff --git a/src/main/java/com/lakala/moss/service/IMossApiService.java b/src/main/java/com/lakala/moss/service/IMossApiService.java new file mode 100644 index 0000000..f9e7d47 --- /dev/null +++ b/src/main/java/com/lakala/moss/service/IMossApiService.java @@ -0,0 +1,47 @@ +package com.lakala.moss.service; + + +import com.lakala.moss.api.ApiReq; +import com.lakala.moss.api.ApiRes; +import com.lakala.moss.api.request.*; +import com.lakala.moss.api.response.*; + +/** + * @author Stephen yu + * @description: api服务接口 + * @date 2025-02-21 + */ +public interface IMossApiService { + /** + * 订单支付 + */ + ApiRes OrderPay(OrderPayReq req); + /** + * 订单查询 + */ + ApiRes OrderQryList(OrderQryListReq req); + /** + * 订单关单 + */ + ApiRes OrderCls(OrderClsReq req); + /** + * 订单退款 + */ + ApiRes OrderRefund(OrderRefundReq req); + /** + * 申请对账单 + */ + ApiRes OrderChkApply(OrderChkApplyReq req); + /** + * 用户标识查询 + */ + ApiRes UseridQry(UseridQryReq req); + /** + * 对账单下载 + */ + boolean ChkDownload(String urlPath,String localDir,String localFileName); + /** + * 订单通知 + */ + ApiRes OrderNotice(ApiReq request); +} diff --git a/src/main/java/com/lakala/moss/service/impl/BaseService.java b/src/main/java/com/lakala/moss/service/impl/BaseService.java new file mode 100644 index 0000000..576f50c --- /dev/null +++ b/src/main/java/com/lakala/moss/service/impl/BaseService.java @@ -0,0 +1,174 @@ +package com.lakala.moss.service.impl; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.lakala.moss.api.*; +import com.lakala.moss.api.request.OrderNoticeReq; +import com.lakala.moss.api.response.OrderNoticeRes; +import com.lakala.moss.util.*; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.*; + +/** + * @author Stephen yu + * @description: 基础服务 + * @date 2025-02-21 + */ +public abstract class BaseService { + + private static Logger logger = LoggerFactory.getLogger(BaseService.class); + + protected ApiReqHead BuildApiRequestHead(String serivceId) { + ApiReqHead head = new ApiReqHead(); + head.setVersionId(PropertiesUtil.getStringByKey("api.versionId")); + head.setServiceId(serivceId); + head.setChannelId(PropertiesUtil.getStringByKey("api.channelId")); + head.setRequestTime(DateUtil.getSystemDateLongStr()); + head.setServiceSn(UUID.randomUUID().toString().replaceAll("-", "")); + head.setBusinessChannel(PropertiesUtil.getStringByKey("api.businessChannel")); + return head; + } + + public ApiRes postService(T requestObj, Class responseClass, String serivceId) { + return invokApiByEncrypt(requestObj, responseClass, serivceId); + } + + private ApiRes invokApiByEncrypt(T requestObj, Class responseClass, String serivceId) { + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + ApiRes responseBean = new ApiRes<>(); + // 组装请求消息头对象 + ApiReqHead reqHead = BuildApiRequestHead(serivceId); + ApiResHead resHead = new ApiResHead(); + resHead.setServiceId(reqHead.getServiceId()); + resHead.setServiceSn(reqHead.getServiceSn()); + resHead.setServiceTime(DateUtil.getSystemDateLongStr()); + try { + Map headMap = ObjectToMapUtils.toMap(reqHead); + TreeMap headTreeMap = new TreeMap<>(); + headTreeMap.putAll(headMap); + // 组装请求消息体对象 + // 生成加密报文,用moss公钥加密 + String reqParams = gson.toJson(requestObj); + logger.info("data=" + reqParams); + String mossPublicKey = PropertiesUtil.getStringByKey("moss.pub.key"); + String requestEncrypted = SecurityUtil.encryptInStringOutBase64(reqParams, mossPublicKey); + logger.info("encData=" + requestEncrypted); + // 组装请求消息签名 + // 签名前字段先按key自然排序,用TreeMap实现 + Map signMap = new TreeMap<>(); + signMap.put("head", gson.toJson(headTreeMap)); + signMap.put("requestEncrypted", requestEncrypted); + // 拼接参与签名的字符串,用商户私钥加签 + String signStr = SecurityUtil.getSignSrcSkipNull(signMap, true, "&"); + String merPrivateKey = PropertiesUtil.getStringByKey("mer.pri.key"); + String sign = SecurityUtil.sign(signStr, merPrivateKey); + // 组装请求消息对象 + Map request = new LinkedHashMap<>(); + request.put("head", headTreeMap); + request.put("requestEncrypted", requestEncrypted); + request.put("sign", sign); + // 最后生成的报文是这样的 + String encReq = gson.toJson(request); + // 发送post消息 + String url = PropertiesUtil.getStringByKey("api.order.url"); + String encRes = HttpsUtil.doPost(url, encReq); + if(StringUtils.isBlank(encRes)){ + resHead.setCode(ApiResCode.PA_INSIDE_ERROR.getCode()); + resHead.setDesc(ApiResCode.PA_INSIDE_ERROR.getDesc() + ",响应消息为空"); + responseBean.setHead(resHead); + return responseBean; + } + Map response = gson.fromJson(encRes,Map.class); + String resSign = (String)response.get("sign"); + String responseEncrypted = (String)response.get("responseEncrypted"); + Map resHeadMap = (Map)response.get("head"); + if(resHeadMap == null || StringUtils.isBlank(resHeadMap.get("code")) ){ + resHead.setCode(ApiResCode.PA_INSIDE_ERROR.getCode()); + resHead.setDesc(ApiResCode.PA_INSIDE_ERROR.getDesc() + ",响应消息头为空"); + responseBean.setHead(resHead); + return responseBean; + } + responseBean.setHead(gson.fromJson(gson.toJson(resHeadMap), ApiResHead.class)); + if(!ApiResCode.SUCCESS.getCode().equals(resHeadMap.get("code"))){ + return responseBean; + } + TreeMap resHeadTreeMap = new TreeMap<>(); + resHeadTreeMap.putAll(resHeadMap); + // 组装响应消息签名 + // 签名前字段先按key自然排序,用TreeMap实现 + Map resSignMap = new TreeMap<>(); + resSignMap.put("head", gson.toJson(resHeadTreeMap)); + resSignMap.put("responseEncrypted", responseEncrypted); + // 拼接参与签名的字符串 + String resSignStr = SecurityUtil.getSignSrcSkipNull(resSignMap, true, "&"); + boolean verifySign = SecurityUtil.verify(resSignStr, resSign, mossPublicKey); + if(!verifySign){ + resHead.setCode(ApiResCode.PA_INSIDE_ERROR.getCode()); + resHead.setDesc(ApiResCode.PA_INSIDE_ERROR.getDesc() + ",响应消息验签失败"); + responseBean.setHead(resHead); + return responseBean; + } + // 用商户的私要解密数据 + if(StringUtils.isNotBlank(responseEncrypted)){ + String decData = SecurityUtil.decryptInBase64OutString(responseEncrypted, merPrivateKey); + logger.info("decData={}",decData); + responseBean.setResponse(gson.fromJson(decData, responseClass)); + } + } catch (Exception e) { + logger.error("invokApi error,e={}", e); + resHead.setCode(ApiResCode.PA_UNKNOWN_ERROR.getCode()); + resHead.setDesc(ApiResCode.PA_UNKNOWN_ERROR.getDesc()+ ",e=" + e.getMessage()); + responseBean.setHead(resHead); + } + return responseBean; + } + + public ApiRes notifyServiceByDecrypt(ApiReq request, Class responseClass) { + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + ApiRes res = new ApiRes<>(); + ApiResHead resHead = new ApiResHead(); + resHead.setServiceId(request.getHead().getServiceId()); + resHead.setServiceSn(request.getHead().getServiceSn()); + resHead.setServiceTime(DateUtil.getSystemDateLongStr()); + try { + Map headMap = ObjectToMapUtils.toMap(request.getHead()); + TreeMap headTreeMap = new TreeMap<>(); + headTreeMap.putAll(headMap); + // 组装响应消息签名 + // 签名前字段先按key自然排序,用TreeMap实现 + Map signMap = new TreeMap<>(); + signMap.put("head", gson.toJson(headTreeMap)); + signMap.put("requestEncrypted", request.getRequestEncrypted()); + // 拼接参与签名的字符串 + String mossPublicKey = PropertiesUtil.getStringByKey("moss.pub.key"); + String signStr = SecurityUtil.getSignSrcSkipNull(signMap, true, "&"); + logger.info("signStr={}", signStr); + boolean verifySign = SecurityUtil.verify(signStr, request.getSign(), mossPublicKey); + if(!verifySign){ + resHead.setCode(ApiResCode.PA_INSIDE_ERROR.getCode()); + resHead.setDesc(ApiResCode.PA_INSIDE_ERROR.getDesc() + ",请求消息验签失败"); + res.setHead(resHead); + return res; + } + // 用商户的私要解密数据 + if(StringUtils.isNotBlank(request.getRequestEncrypted())){ + String merPrivateKey = PropertiesUtil.getStringByKey("mer.pri.key"); + String decData = SecurityUtil.decryptInBase64OutString(request.getRequestEncrypted(), merPrivateKey); + logger.info("decData={}",decData); + } + resHead.setCode(ApiResCode.SUCCESS.getCode()); + resHead.setDesc(ApiResCode.SUCCESS.getDesc()); + res.setHead(resHead); + } catch (Exception e) { + logger.error("invokApi error,e={}", e); + resHead.setCode(ApiResCode.PA_UNKNOWN_ERROR.getCode()); + resHead.setDesc(ApiResCode.PA_UNKNOWN_ERROR.getDesc()+ ",e=" + e.getMessage()); + res.setHead(resHead); + } finally { + + } + return res; + } +} diff --git a/src/main/java/com/lakala/moss/service/impl/MossApiServiceImpl.java b/src/main/java/com/lakala/moss/service/impl/MossApiServiceImpl.java new file mode 100644 index 0000000..cbbc7f8 --- /dev/null +++ b/src/main/java/com/lakala/moss/service/impl/MossApiServiceImpl.java @@ -0,0 +1,49 @@ +package com.lakala.moss.service.impl; + +import com.lakala.moss.api.*; +import com.lakala.moss.api.request.*; +import com.lakala.moss.api.response.*; +import com.lakala.moss.service.IMossApiService; +import com.lakala.moss.util.CustomUrlUtil; + +/** + * @author Stephen yu + * @description: api服务接口实现 + * @date 2025-02-21 + */ +public class MossApiServiceImpl extends BaseService implements IMossApiService { + @Override + public ApiRes OrderPay(OrderPayReq req) { + return super.postService(req,OrderPayRes.class,"lfops.moss.order.pay"); + } + @Override + public ApiRes OrderQryList(OrderQryListReq req) { + return super.postService(req,OrderQryListRes.class,"lfops.moss.order.qry"); + } + @Override + public ApiRes OrderCls(OrderClsReq req) { + return super.postService(req,OrderClsRes.class,"lfops.moss.order.cls"); + } + @Override + public ApiRes OrderRefund(OrderRefundReq req) { + return super.postService(req,OrderRefundRes.class,"lfops.moss.order.ref"); + } + @Override + public ApiRes OrderChkApply(OrderChkApplyReq req) { + return super.postService(req,OrderChkApplyRes.class,"lfops.moss.order.chk.apply"); + } + @Override + public ApiRes UseridQry(UseridQryReq req) { + return super.postService(req,UseridQryRes.class,"lfops.moss.userid.qry"); + } + + @Override + public boolean ChkDownload(String urlPath, String localDir, String localFileName) { + return CustomUrlUtil.download(urlPath,localDir,localFileName); + } + + @Override + public ApiRes OrderNotice(ApiReq request) { + return super.notifyServiceByDecrypt(request,OrderNoticeRes.class); + } +} diff --git a/src/main/java/com/lakala/moss/util/CustomUrlUtil.java b/src/main/java/com/lakala/moss/util/CustomUrlUtil.java new file mode 100644 index 0000000..e826098 --- /dev/null +++ b/src/main/java/com/lakala/moss/util/CustomUrlUtil.java @@ -0,0 +1,82 @@ +package com.lakala.moss.util; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; + +/** + * 工具类 - url下载或上传文件 + * + * @author Stephen yu + * @date 2025-02-26 + */ +public class CustomUrlUtil { + private static final Logger logger = LoggerFactory.getLogger(CustomUrlUtil.class); + + /** + * + * @param urlPath url链接字符串 + */ + private static URLConnection connectUrlServer(String urlPath){ + logger.info("connect params [urlPath="+urlPath+"]"); + URLConnection conn = null; + try { + URL url = new URL(urlPath); + conn = url.openConnection(); + + } catch (MalformedURLException e) { + logger.error("connect failed,err msg = {}",e.getMessage()); + } catch (IOException e) { + logger.error("connect failed,err msg = {}",e.getMessage()); + } + return conn; + } + + /** + * 文件下载 + * @param urlPath urlPath地址 + * @param localDir 本地路径 + * @param localFileName 本地文件名称 + */ + public static boolean download(String urlPath,String localDir,String localFileName){ + URLConnection conn = connectUrlServer(urlPath); + if (conn == null){ + return false; + } + InputStream is = null; + FileOutputStream os = null; + try { + is = conn.getInputStream(); + byte[] bs = new byte[1024]; + int len; + File file = new File(localDir+localFileName); + os = new FileOutputStream(file); + while ((len = is.read(bs)) != -1){ + os.write(bs,0,len); + } + logger.info("从Url[{}]读取文件到[{}]成功", urlPath, localDir+localFileName); + + } catch (IOException e) { + logger.error("处理文件流失败.err msg = {}",e.getMessage()); + return false; + } finally { + try { + os.close(); + is.close(); + } catch (IOException e) { + logger.error("关闭文件流失败.err msg = {}",e.getMessage()); + return false; + } + } + return true; + } + +} diff --git a/src/main/java/com/lakala/moss/util/DateUtil.java b/src/main/java/com/lakala/moss/util/DateUtil.java new file mode 100644 index 0000000..35a096e --- /dev/null +++ b/src/main/java/com/lakala/moss/util/DateUtil.java @@ -0,0 +1,669 @@ +package com.lakala.moss.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +import static java.util.Calendar.*; + +/** + * @author Stephen yu + * @description: 日期工具类 + * @date 2025-02-21 + */ +public class DateUtil { + private static Logger logger = LoggerFactory.getLogger(DateUtil.class); + private static final Calendar CAL = getInstance(); + private static final Calendar TMP = getInstance(); + public static final String FORMAT1 = "yyyyMMdd"; + public static final String FORMAT2 = "yyyy-MM-dd"; + public static final String FORMAT3 = "yyyyMMddHHmmss"; + public static final String FORMAT4 = "yyyy-MM-dd HH:mm:ss"; + public static final String FORMAT5 = "MMddHHmmss"; + public static final String FORMAT6 = "yyMMddHHmmss"; + public static final int YEAR = Calendar.YEAR; + public static final int MONTH = Calendar.MONTH; + public static final int DAY_OF_MONTH = Calendar.DAY_OF_MONTH; + public static final int HOUR = Calendar.HOUR; + public static final int MINUTE = Calendar.MINUTE; + public static final int SECOND = Calendar.SECOND; + + // 静态工具类,防误生成 + private DateUtil() { + throw new UnsupportedOperationException(); + } + + /** + *

Title: isThisMonth

+ *

Description: 判断给定日期date是否在当前月内

+ * + * @param date 给定日期 + * @return boolean + */ + public static boolean isThisMonth(Date date) { + return isInRange(date, MONTH); + } + + /** + *

Title: getDateByDayOfMonth

+ *

Description: 查找指定日期的下一个提醒日

+ * + * @param date 指定日期 + * @param day 提醒日 + * @return Date + */ + public static Date getDateByDayOfMonth(Date date, int day) { + synchronized (CAL) { + CAL.setTime(date); + int today = CAL.get(DAY_OF_MONTH); + if (day < today) { + CAL.add(MONTH, 1); + } + CAL.set(DAY_OF_MONTH, day); + return CAL.getTime(); + } + } + + /** + *

Title: getDateByDayOfMonth

+ *

Description: 查找当前日期的下一个提醒日

+ * + * @param day 提醒日 + * @return Date + */ + public static Date getDateByDayOfMonth(int day) { + return getDateByDayOfMonth(new Date(), day); + } + + /** + *

Title: getTheAppointedDayOfTheMonth

+ *

Description: 根据指定日期和月份换回当月的提醒日期,当提醒日期day大于本月最大天数时,取最大天数。

+ * + * @param month 指定的月份(0-11) + * @param day 指定的提醒日(1-31) + * @return int 1-31 + */ + public static int getTheAppointedDayOfTheMonth(int month, int day) { + synchronized (CAL) { + CAL.set(MONTH, month); + int lastDay = CAL.getActualMaximum(DAY_OF_MONTH); + return day > lastDay ? lastDay : day; + } + } + + /** + *

Title: getTheAppointedDayOfTheMonth

+ *

Description: 根据指定日期返回当前月份的提醒日期,当提醒日期day大于本月最大天数时,取最大天数。

+ * + * @param day 指定的提醒日(1-31) + * @return int 1-31 + */ + public static int getTheAppointedDayOfTheMonth(int day) { + synchronized (CAL) { + resetCalendarWithNowTime(); + int lastDay = CAL.getActualMaximum(DAY_OF_MONTH); + return day > lastDay ? lastDay : day; + } + } + + /** + *

Title: getDayOfMonthOfToday

+ *

Description: 获取当前日期在本月中是第几天

+ * + * @return int 1-31 + */ + public static int getDayOfMonthOfToday() { + synchronized (CAL) { + resetCalendarWithNowTime(); + return CAL.get(DAY_OF_MONTH); + } + } + + /** + *

Title: getFate

+ *

Description: 根据提醒日和指定日期,返回提醒日和指定日期间隔天数,提醒日当天返回0。

+ * + * @param day 提醒日 + * @param date 指定日期 + * @return int 0-30 + */ + public static int getFate(int day, Date date) { + day = getTheAppointedDayOfTheMonth(day); + synchronized (CAL) { + CAL.setTime(date); + int today = CAL.get(DAY_OF_MONTH); + int max = CAL.getActualMaximum(DAY_OF_MONTH); + return (day - today + max) % max; + } + } + + /** + *

Title: getFate

+ *

Description: 根据提醒日,返回下次提醒日距今的天数,提醒日当天返回0。

+ * + * @param day 提醒日 + * @return int 0-30 + */ + public static int getFate(int day) { + return getFate(day, new Date()); + } + + /** + *

Title: resetCalendarWithNowTime

+ *

Description: 将cal重置为当前时间。

+ */ + private static void resetCalendarWithNowTime() { + synchronized (CAL) { + CAL.setTime(new Date()); + } + } + + /** + *

Title: getFormatDate

+ *

Description: 把指定日期date转换为指定格式format的日期字符串

+ * + * @param date 指定日期 + * @param pattern 指定格式,可从静态常量中获取 + * @return String + */ + public static String getFormatDateStr(Date date, String pattern) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern); + return simpleDateFormat.format(date); + } + + /** + *

Title: getFormatDateStr

+ *

Description: 将传入的fm1格式的时间字符串date,转换为fm2格式的字符串类型

+ * + * @param date 指定日期 + * @param fm1 fm1格式 + * @param fm2 fm2格式 + * @return String + */ + public static String getFormatDateStr(String date, String fm1, String fm2) { + String oDate = null; + SimpleDateFormat fmt = new SimpleDateFormat(fm1); + try { + Date outDate = fmt.parse(date); + fmt.applyPattern(fm2); + oDate = fmt.format(outDate); + } catch (ParseException e) { + logger.error("{}", e); + } + return oDate; + } + + /** + *

Title: getSystemDateLongStr

+ *

Description: 获取当前日期长字符串格式

+ * + * @return String yyyyMMddHHmmss + */ + public static String getSystemDateLongStr() { + Date sysDate = new Date(); + return getFormat3DateStr(sysDate); + } + + /** + *

Title: getSystemDateStr

+ *

Description: 获取当前日期字符串格式

+ * + * @return String yyyyMMdd + */ + public static String getSystemDateStr() { + Date sysDate = new Date(); + return getFormat1DateStr(sysDate); + } + + /** + *

Title: getSystemDateStr

+ *

Description: 获取当前日期指定字符串格式

+ * + * @param pattern 指定字符串格式 + * @return String + */ + public static String getSystemDateStr(String pattern) { + return getFormatDateStr(new Date(), pattern); + } + + /** + *

Title: getFormat1DateStr

+ *

Description: 获取指定日期date字符串格式

+ * + * @param date 指定日期 + * @return String yyyyMMdd + */ + public static String getFormat1DateStr(Date date) { + return getFormatDateStr(date, FORMAT1); + } + + /** + *

Title: getFormat2DateStr

+ *

Description: 将日期类型date转换为yyyy-MM-dd字符串

+ * + * @param date 目标日期 + * @return String yyyy-MM-dd + */ + public static String getFormat2DateStr(Date date) { + return getFormatDateStr(date, FORMAT2); + } + + /** + *

Title: getFormat3DateStr

+ *

Description: 将日期类型date转换为yyyyMMddHHmmss字符串

+ * + * @param date 目标日期 + * @return String yyyyMMddHHmmss + */ + public static String getFormat3DateStr(Date date) { + return getFormatDateStr(date, FORMAT3); + } + + /** + *

Title: getFormat4DateStr

+ *

Description: 将日期类型date转换为yyyy-MM-dd HH:mm:ss字符串

+ * + * @param date 目标日期 + * @return String yyyy-MM-dd HH:mm:ss + */ + public static String getFormat4DateStr(Date date) { + return getFormatDateStr(date, FORMAT4); + } + + /** + *

Title: getFormat5DateStr

+ *

Description: 将日期类型date转换为MMddHHmmss字符串

+ * + * @param date 目标日期 + * @return String MMddHHmmss + */ + public static String getFormat5DateStr(Date date) { + return getFormatDateStr(date, FORMAT5); + } + + /** + *

Title: getFormat6DateStr

+ *

Description: 将日期类型date转换为yyMMddHHmmss字符串

+ * + * @param date 目标日期 + * @return String MMddHHmmss + */ + public static String getFormat6DateStr(Date date) { + return getFormatDateStr(date, FORMAT6); + } + + /** + *

Title: getDiffBetweenTwoDate

+ *

Description: 计算两个日期差几天,也可比较两个日期谁在前,谁在后

+ * + * @param firstDate 第一个日期 + * @param secondDate 第二个日期 + * @return int 天数。如果secondDate在firstDate之前,返回一个负整数;反之返回正整数 + */ + public static int getDiffBetweenTwoDate(Date firstDate, Date secondDate) { + long seconds = secondDate.getTime() - firstDate.getTime();// 终止日期-起始日期=毫秒 + int diff = (int) (seconds / (24 * 60 * 60 * 1000));// 再除以每天多少毫秒(24*60*60*1000)=差几天 + return diff; + } + + /** + *

Title: getDiffBetweenTwoDate

+ *

Description: 计算两个日期差几天,也可比较两个日期谁在前,谁在后

+ * + * @param firstDate 第一个日期 yyyyMMdd + * @param secondDate 第二个日期 yyyyMMdd + * @return int 天数。如果secondDate在firstDate之前,返回一个负整数;反之返回正整数 + */ + public static int getDiffBetweenTwoDate(String firstDate, String secondDate) { + return getDiffBetweenTwoDate(firstDate, secondDate, FORMAT1); + } + + /** + *

Title: getDiffBetweenTwoDate

+ *

Description: 计算两个日期差几天,也可比较两个日期谁在前,谁在后

+ * + * @param firstDate 第一个日期 + * @param secondDate 第二个日期 + * @param format 日期样式 + * @return int 如果secondDate在firstDate之前,返回一个负整数;反之返回正整数 + */ + public static int getDiffBetweenTwoDate(String firstDate, String secondDate, String format) { + SimpleDateFormat myFormatter = new SimpleDateFormat(format);// 计算两天之差 + Date date1 = null; + Date date2 = null; + int diff = 0; + try { + date1 = myFormatter.parse(firstDate);// 起始日期 + date2 = myFormatter.parse(secondDate);// 终止日期 + diff = getDiffBetweenTwoDate(date1, date2); + } catch (ParseException e) { + logger.error("{}", e); + }// 起始日期 + return diff; + } + + /** + *

Title: isLeapYear

+ *

Description: 判断目标日期所在年是否为闰年

+ * + * @param date 目标日期 + * @return boolean + */ + public static boolean isLeapYear(Date date) { + synchronized (CAL) { + CAL.setTime(date); + int year = CAL.get(YEAR); + if (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)) { + return true; + } + return false; + } + } + + /** + *

Title: getDifferMonth

+ *

Description: 计算两个日期相差的月份

+ * + * @param startDate 起始日期 + * @param endDate 结束日期 + * @return int 正值:startDate先于endDate;负值:startDate后于endDate + */ + public static int getDifferMonth(Date startDate, Date endDate) { + synchronized (CAL) { + CAL.setTime(startDate); + TMP.setTime(endDate); + int differYear = TMP.get(YEAR) - CAL.get(YEAR); + int differMonth = TMP.get(MONTH) - CAL.get(MONTH); + return differYear * 12 + differMonth; + } + } + + /** + *

Title: getDateByAddYear

+ *

Description: 指定日期date加year年

+ * + * @param date 指定日期 + * @param year 指定年份 + * @return Date + */ + public static Date getDateByAddYear(Date date, int year) { + return getNextRangeDate(date, YEAR, year); + } + + /** + *

Title: getDateByAddYear

+ *

Description: 当前日期加year年

+ * + * @param year 指定年份 + * @return Date + */ + public static Date getDateByAddYear(int year) { + return getDateByAddYear(new Date(), year); + } + + /** + *

Title: getDateBeforeXDay

+ *

Description: 获取指定日期前n日的日期时间

+ * + * @param date 指定日期 + * @param n 提前天数 + * @param format 格式化日期字符串 + * @return String + */ + public static String getDateBeforeXDay(Date date, int n, String format) { + Date d = getNextRangeDate(date, DAY_OF_MONTH, -n); + SimpleDateFormat sdf = new SimpleDateFormat(format); + return sdf.format(d); + } + + /** + *

Title: getDate

+ *

Description: 日期字符串转换为日期

+ * + * @param dateStr 日期字符串 + * @param format 转换格式字符串 + * @return Date + */ + public static Date getDate(String dateStr, String format) { + SimpleDateFormat sdf = new SimpleDateFormat(format); + Date date = null; + try { + date = sdf.parse(dateStr); + } catch (ParseException e) { + logger.error("{}", e); + } + return date; + } + + /** + *

Title: getBeforeXDay

+ *

Description: 返回当月指定日期的前X天的值

+ * + * @param target 指定天数 + * @param x 提前天数 + * @return int + */ + public static int getBeforeXDay(int target, int x) { + synchronized (CAL) { + resetCalendarWithNowTime(); + CAL.set(DAY_OF_MONTH, target); + CAL.add(DAY_OF_MONTH, -x); + return CAL.get(DAY_OF_MONTH); + } + } + + /** + *

Title: dateCompare

+ *

Description: 比较两个日期是否在某个时间范围内

+ * + * @param date1 日期一 + * @param date2 日期二 + * @param range 范围,Calendar常量或本类常量 + * @param val 范围值 + * @return boolean + */ + public static boolean dateCompare(Date date1, Date date2, int range, int val) { + synchronized (CAL) { + CAL.setTime(date1); + TMP.setTime(date2); + if (CAL.before(TMP)) { + CAL.add(range, val); + return CAL.after(TMP); + } else if (CAL.after(TMP)) { + TMP.add(range, val); + return CAL.before(TMP); + } else { + return true; + } + } + } + + /** + *

Title: dateCompare

+ *

Description: 判断指定日期与当前日期是否在某个时间范围内

+ * + * @param date 指定日期 + * @param range 范围,Calendar常量或本类常量 + * @param val 范围值 + * @return boolean + */ + public static boolean dateCompare(Date date, int range, int val) { + return dateCompare(date, new Date(), range, val); + } + + /** + *

Title: isInRange

+ *

Description: 判断指定日期与当前日期是否在指定范围内,当前日期取极值。

+ * + * @param date 指定日期 + * @param range 范围,Calendar常量或本类常量 + * @param val 范围值 + * @return boolean + */ + public static boolean isInRange(Date date, int range, int val) { + synchronized (CAL) { + boolean res = false; + TMP.setTime(date); + resetCalendarWithNowTime(); + resetCalendarByRange(CAL, range); + res = TMP.after(CAL); + CAL.add(range, val); + res = res && TMP.before(CAL); + return res; + } + } + + /** + *

Title: resetCalendarByRange

+ *

Description: 按指定范围range重置cal时间值

+ * + * @param cal 时间类 + * @param range 范围,Calendar常量或本类常量 + */ + public static void resetCalendarByRange(Calendar cal, int range) { + switch (range) { + case YEAR: + cal.set(MONTH, CAL.getActualMinimum(MONTH)); + case MONTH: + cal.set(DAY_OF_MONTH, CAL.getActualMinimum(DAY_OF_MONTH)); + case DAY_OF_MONTH: + cal.set(HOUR_OF_DAY, CAL.getActualMinimum(HOUR_OF_DAY)); + case HOUR_OF_DAY: + cal.set(MINUTE, CAL.getActualMinimum(MINUTE)); + case MINUTE: + cal.set(SECOND, CAL.getActualMinimum(SECOND)); + case SECOND: + cal.set(MILLISECOND, CAL.getActualMinimum(MILLISECOND)); + } + } + + /** + *

Title: isInRange

+ *

Description: 判断指定日期与当前日期是否在1个单位的范围内,当前日期取极值。

+ * + * @param date 指定日期 + * @param range 范围,Calendar常量或本类常量 + * @return boolean + */ + public static boolean isInRange(Date date, int range) { + return isInRange(date, range, 1); + } + + /** + *

Title: isSameDay

+ *

Description: 判断指定日期与当前日期是否在同一天内。

+ * + * @param date 指定日期 + * @return boolean + */ + public static boolean isSameDay(Date date) { + return isInRange(date, DAY_OF_MONTH); + } + + /** + *

Title: getNextDate

+ *

Description: 获取指定日期下一天的日期

+ * + * @param start 指定日期 + * @return Date + */ + public static Date getNextDate(Date start) { + return getNextDate(start, 1); + } + + /** + *

Title: getNextDate

+ *

Description: 获取指定日期下days天的日期

+ * + * @param start 指定日期 + * @param days 天数 + * @return Date + */ + public static Date getNextDate(Date start, int days) { + synchronized (CAL) { + CAL.setTime(start); + CAL.add(DAY_OF_MONTH, days); + return CAL.getTime(); + } + } + + /** + *

Title: getNextRangeDate

+ *

Description: 获取指定日期下val个单位指定范围range的日期

+ * + * @param start 指定日期 + * @param range 范围,Calendar常量或本类常量 + * @param val 范围值 + * @return Date + */ + public static Date getNextRangeDate(Date start, int range, int val) { + synchronized (CAL) { + CAL.setTime(start); + CAL.add(range, val); + return CAL.getTime(); + } + } + + /** + *

Title: getDateStrFormatPlusHour

+ *

Description: 把日期转换为指定格式的日期字符串

+ * + * @param date 指定日期 + * @param format 格式化字符串 + * @param hour 添加时间 + * @return String + */ + public static String getDateStrFormatPlusHour(Date date, String format, + int hour) { + Date nextDate = getNextRangeDate(date, HOUR_OF_DAY, hour); + return getFormatDateStr(nextDate, format); + } + + public static String getDateMinu() {//获取当前时间与当日23:59:59的相差分钟数 + Date lastDay = new Date(); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(lastDay); + calendar.set(Calendar.HOUR_OF_DAY, 23); + calendar.set(Calendar.MINUTE, 50); + calendar.set(Calendar.SECOND, 00); + Date d = calendar.getTime(); + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + long currentTime = 0; + try { + currentTime = df.parse(df.format(new Date())).getTime(); + long createTime = df.parse(df.format(d)).getTime(); + long diff = (createTime - currentTime) / 1000 / 60; + if (diff > 999) { + diff = 999; + } + String minu = String.valueOf(diff); + return minu; + } catch (ParseException e) { + e.printStackTrace(); + return "10"; + } + } + + public static void main(String[] args) throws ParseException { + Date lastDay = new Date(); +// Calendar calendar = Calendar.getInstance(); +// calendar.setTime(lastDay); +// calendar.set(Calendar.HOUR_OF_DAY, 23); +// calendar.set(Calendar.MINUTE, 59); +// calendar.set(Calendar.SECOND, 59); +// Date d = calendar.getTime(); +// System.out.println(""+ d); +// SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); +// System.out.println(""+ df.format(new Date())); +// long currentTime = df.parse(df.format(new Date())).getTime(); +// //从对象中拿到时间 +// long createTime = df.parse(df.format(d)).getTime(); +// long diff = (createTime-currentTime)/1000/60; +// System.out.println("当前系统时间为:"+currentTime+"下单时间为:"+createTime+"两个时间差为:"+diff); + System.out.println(getNextDate(lastDay, -1)); + System.out.println(getDateMinu()); + } +} diff --git a/src/main/java/com/lakala/moss/util/HttpsUtil.java b/src/main/java/com/lakala/moss/util/HttpsUtil.java new file mode 100644 index 0000000..1fac82f --- /dev/null +++ b/src/main/java/com/lakala/moss/util/HttpsUtil.java @@ -0,0 +1,120 @@ +package com.lakala.moss.util; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.message.BasicHeader; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Stephen yu + * @description: HTTPS工具类 + * @date 2025-02-21 + */ +public class HttpsUtil { + + private static Logger logger = LoggerFactory.getLogger(HttpsUtil.class); + + private HttpsUtil(){} + + /** + * post请求 + * @param url + * @param jsonStrParams + * @return + * @throws Exception + */ + public static String doPost(String url, String jsonStrParams) throws Exception { + logger.info("url=" + url); + logger.info("request message=" + jsonStrParams); + HttpClient httpClient = null; + HttpPost httpPost = null; + String result = null; + HttpResponse response = null; + + httpClient = new SSLClient(); + httpPost = new HttpPost(url); + httpPost.addHeader("Content-Type", "application/json"); + StringEntity se = new StringEntity(jsonStrParams, "UTF-8"); + se.setContentType("text/json"); + se.setContentEncoding(new BasicHeader("Content-Type", "application/json")); + httpPost.setEntity(se); + response = httpClient.execute(httpPost); + if (response != null) { + HttpEntity resEntity = response.getEntity(); + if (resEntity != null) { + result = EntityUtils.toString(resEntity, "UTF-8"); + } + } + logger.info("response message=" + result); + + return result; + } + + /** + * post请求 + * @param url + * @param jsonStrParams + * @return + * @throws Exception + */ + public static String doPost2(String url, String jsonStrParams) throws Exception { + + logger.info("request:" + jsonStrParams); + HttpClient httpClient = null; + HttpPost httpPost = null; + String result = null; + HttpResponse response = null; + + httpClient = new SSLClient(); + httpPost = new HttpPost(url); + httpPost.addHeader("Content-Type", "application/json"); + httpPost.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36"); + StringEntity se = new StringEntity(jsonStrParams, "UTF-8"); + se.setContentType("text/json"); + se.setContentEncoding(new BasicHeader("Content-Type", "application/json")); + httpPost.setEntity(se); + response = httpClient.execute(httpPost); + if (response != null) { + HttpEntity resEntity = response.getEntity(); + if (resEntity != null) { + result = EntityUtils.toString(resEntity, "UTF-8"); + } + } + logger.info("response:" + result); + + return result; + } + + /** + * get请求 + * @param url + * @return + * @throws Exception + */ + public static String doGet(String url) throws Exception { + logger.info("对账文件Url : " + url); + HttpClient httpClient = null; + HttpGet httpGet = null; + String result = null; + HttpResponse response = null; + + httpClient = new SSLClient(); + httpGet = new HttpGet(url); + response = httpClient.execute(httpGet); + if (response != null) { + HttpEntity resEntity = response.getEntity(); + if (resEntity != null) { + result = EntityUtils.toString(resEntity, "GBK"); + } + } + logger.info("对账文件内容 : " + result); + + return result; + } +} \ No newline at end of file diff --git a/src/main/java/com/lakala/moss/util/ObjectToMapUtils.java b/src/main/java/com/lakala/moss/util/ObjectToMapUtils.java new file mode 100644 index 0000000..434884b --- /dev/null +++ b/src/main/java/com/lakala/moss/util/ObjectToMapUtils.java @@ -0,0 +1,167 @@ +package com.lakala.moss.util; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Stephen yu + * @description: 对象转成Map工具类 + * @date 2025-02-21 + */ +public class ObjectToMapUtils { + + /** + * 将目标对象的所有属性转换成Map对象 + * + * @param target 目标对象 + * @return Map + */ + public static Map toMap(Object target) { + return toMap(target, false); + } + + /** + * 将目标对象的所有属性转换成Map对象 + * + * @param target 目标对象 + * @param ignoreParent 是否忽略父类的属性 + * @return Map + */ + public static Map toMap(Object target, boolean ignoreParent) { + return toMap(target, ignoreParent, false); + } + + /** + * 将目标对象的所有属性转换成Map对象 + * + * @param target 目标对象 + * @param ignoreParent 是否忽略父类的属性 + * @param ignoreEmptyValue 是否不把空值添加到Map中 + * @return Map + */ + public static Map toMap(Object target, boolean ignoreParent, boolean ignoreEmptyValue) { + return toMap(target, ignoreParent, ignoreEmptyValue, new String[0]); + } + + /** + * 将目标对象的所有属性转换成Map对象 + * + * @param target 目标对象 + * @param ignoreParent 是否忽略父类的属性 + * @param ignoreEmptyValue 是否不把空值添加到Map中 + * @param ignoreProperties 不需要添加到Map的属性名 + * @return Map + */ + public static Map toMap(Object target, boolean ignoreParent, boolean ignoreEmptyValue, + String... ignoreProperties) { + final Map map = new HashMap(); + if (target == null) { + return map; + } + final Map ignoreMap = getIgnorePropertiesMap(ignoreProperties); + final List fields = getClassFields(ignoreParent, target.getClass()); + for (Field field : fields) { + T value = getFieldsValue(target, field); + if (isIgnoreEmpty(value, ignoreEmptyValue) || isIgnoreProperty(field.getName(), ignoreMap)) { + continue; + } + map.put(field.getName(), value); + } + return map; + } + + /** + * 获得忽略的属性Map条件 + * + * @param ignoreProperties 待忽略的属性 + * @return + */ + private static Map getIgnorePropertiesMap(final String[] ignoreProperties) { + final Map resultMap = new HashMap(); + for (String ignoreProperty : ignoreProperties) { + resultMap.put(ignoreProperty, null); + } + return resultMap; + } + + /** + * 获取类实例的属性 + * + * @param ignoreParent 是否忽略父类属性 + * @param clazz 类名 + * @return + */ + private static List getClassFields(boolean ignoreParent, Class clazz) { + final List fields = new ArrayList(); + fields.addAll(Arrays.asList(clazz.getDeclaredFields())); + if (!ignoreParent) { + fields.addAll(getParentClassFields(fields, clazz.getSuperclass())); + } + return fields; + } + + /** + * 获取类实例的父类的属性 + * + * @param fields 类实例的属性 + * @param clazz 类名 + * @return + */ + private static List getParentClassFields(List fields, Class clazz) { + fields.addAll(Arrays.asList(clazz.getDeclaredFields())); + if (clazz.getSuperclass() == null) { + return fields; + } + return getParentClassFields(fields, clazz.getSuperclass()); + } + + /** + * 获取类实例的属性值 + * + * @param target 目标对象 + * @param field 目标对象属性 + * @return + */ + @SuppressWarnings("unchecked") + private static T getFieldsValue(Object target, Field field) { + field.setAccessible(true); + T value = null; + try { + value = (T) field.get(target); + } catch (Exception e) { + e.printStackTrace(); + } + return value; + } + + /** + * 是否忽略属性 + * + * @param fieldName 属性字段名 + * @param ignoreMap 忽略属性MAP + * @return + */ + private static boolean isIgnoreProperty(String fieldName, final Map ignoreMap) { + return ignoreMap.containsKey(fieldName); + } + + /** + * 是否忽略空值 + * + * @param value 属性值 + * @param ignoreEmptyValue 是否不把空值添加到Map中 + * @return + */ + private static boolean isIgnoreEmpty(T value, boolean ignoreEmptyValue) { + boolean isEmpty = (value == null || value.toString().equals("")); + boolean isCollectionEmpty = (value instanceof Collection && ((Collection) value).isEmpty()); + boolean isMapEmpty = (value instanceof Map && ((Map) value).isEmpty()); + return ignoreEmptyValue && (isEmpty || isCollectionEmpty || isMapEmpty); + } + +} diff --git a/src/main/java/com/lakala/moss/util/PropertiesUtil.java b/src/main/java/com/lakala/moss/util/PropertiesUtil.java new file mode 100644 index 0000000..d24a3fa --- /dev/null +++ b/src/main/java/com/lakala/moss/util/PropertiesUtil.java @@ -0,0 +1,183 @@ +package com.lakala.moss.util; + +import java.io.*; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; + +/** + * @author Stephen yu + * @description: 配置文件util + * @date 2025-02-21 + */ +public class PropertiesUtil { + + /** + * 私有化构造函数,防止类被实例化 + */ + private PropertiesUtil(String filePath, String encode) { + File file = new File(filePath); + File[] array = file.listFiles(); + for(File childfile : array) { + String childFileName = childfile.getName(); + if(!childFileName.endsWith("properties")) continue; + loadConfigProperties(childFileName, encode); + } + } + /** + * 存放配置文件的所有的key-value + * 以这种方式存储是为了防止多个配置文件间存在key冲突 + */ + private Map> allParam = new HashMap>(); + /** + * 默认的properties文件名 + */ + private static final String DEFAULT_PROPERTIES_NAME = "moss-sdk.properties"; + /** + * 解析properties文件默认编码 + */ + private static final String DEFAULT_ENCODE = "UTF-8"; + /** + * 解析properties文件编码 + */ + private static String fileEncode = null; + /** + * properties文件所在的地址 + */ + private static String propertiesParentPath = null; + /** + * 根据文件名称-key,返回相应key的值 + * 文件名默认为config.properties,文件编码默认为UTF-8 + */ + public static Integer getIntegerByKey(String key) { + return Integer.parseInt(getStringByKey(DEFAULT_PROPERTIES_NAME, key, DEFAULT_ENCODE)); + } + /** + * 根据文件名称-key,返回相应key的值,文件编码默认为UTF-8 + * @param fileName 文件名 + * @param key properties文件中key的名称 + */ + public static Integer getIntegerByKey(String fileName, String key) { + return Integer.parseInt(getStringByKey(fileName, key, DEFAULT_ENCODE)); + } + /** + * 根据文件名称-key,返回相应key的值 + * @param fileName 文件名 + * @param key properties文件中key的名称 + * @param encode 文件编码 + */ + public static Integer getIntegerByKey(String fileName, String key, String encode) { + return Integer.parseInt(getStringByKey(fileName, key, encode)); + } + /** + * 根据文件名称-key,返回相应key的值 + * 文件名默认为config.properties,文件编码默认为UTF-8 + */ + public static Boolean getBooleanByKey(String key) { + return Boolean.parseBoolean(getStringByKey(DEFAULT_PROPERTIES_NAME, key, DEFAULT_ENCODE)); + } + /** + * 根据文件名称-key,返回相应key的值,文件编码默认为UTF-8 + * @param fileName 文件名 + * @param key properties文件中key的名称 + */ + public static Boolean getBooleanByKey(String fileName, String key) { + return Boolean.parseBoolean(getStringByKey(fileName, key, DEFAULT_ENCODE)); + } + /** + * 根据文件名称-key,返回相应key的值 + * @param fileName 文件名 + * @param key properties文件中key的名称 + * @param encode 文件编码 + */ + public static Boolean getBooleanByKey(String fileName, String key, String encode) { + return Boolean.parseBoolean(getStringByKey(fileName, key, encode)); + } + /** + * 根据文件名称-key,返回相应key的值 + * 文件名默认为config.properties,文件编码默认为UTF-8 + */ + public static Long getLongByKey(String key) { + return Long.parseLong(getStringByKey(DEFAULT_PROPERTIES_NAME, key, DEFAULT_ENCODE)); + } + /** + * 根据文件名称-key,返回相应key的值,文件编码默认为UTF-8 + * @param fileName 文件名 + * @param key properties文件中key的名称 + */ + public static Long getLongByKey(String fileName, String key) { + return Long.parseLong(getStringByKey(fileName, key, DEFAULT_ENCODE)); + } + /** + * 根据文件名称-key,返回相应key的值 + * @param fileName 文件名 + * @param key properties文件中key的名称 + * @param encode 文件编码 + */ + public static Long getLongByKey(String fileName, String key, String encode) { + return Long.parseLong(getStringByKey(fileName, key, encode)); + } + /** + * 根据文件名称-key,返回相应key的值 + * 文件名默认为config.properties,文件编码默认为UTF-8 + */ + public static String getStringByKey(String key){ + return getStringByKey(DEFAULT_PROPERTIES_NAME, key, DEFAULT_ENCODE); + } + /** + * 根据文件名,key返回value值,文件编码默认为UTF-8 + */ + public static String getStringByKey(String fileName, String key) { + return getStringByKey(fileName, key, DEFAULT_ENCODE); + } + /** + * 根据文件名,key,编码返回value值 + */ + public static String getStringByKey(String fileName, String key, String encode) { + String propertiesPath = PropertiesUtil.class.getClassLoader() + .getResource(fileName).getPath().replace(fileName, ""); + propertiesParentPath = propertiesPath; + fileEncode = encode; + return PropertiesUtilHolder.instance.allParam.get(fileName).get(key); + } + /** + * 静态内部类,单例模式,保证只有一个实例变量 + */ + private static class PropertiesUtilHolder { + private static PropertiesUtil instance = new PropertiesUtil(propertiesParentPath, fileEncode); + } + /** + * 加载配置文件,需要进行加锁 + */ + private void loadConfigProperties(String fileName, String encode) { + InputStream in = null; + try { + Properties p = new Properties(); + File file = new File(fileName); + if(file.exists()) { + p.load(new FileInputStream(file)); + } else { + in = PropertiesUtil.class.getClassLoader().getResourceAsStream(fileName); + // 解决中文乱码 + BufferedReader bf = new BufferedReader(new InputStreamReader(in, encode)); + p.load(bf); + } + Set> allKey = p.entrySet(); + HashMap paramMap = new HashMap(); + for (Entry entry : allKey) { + paramMap.put(String.valueOf(entry.getKey()), String.valueOf(entry.getValue())); + } + allParam.put(fileName, paramMap); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if(in != null) in.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/com/lakala/moss/util/SSLClient.java b/src/main/java/com/lakala/moss/util/SSLClient.java new file mode 100644 index 0000000..bc82b17 --- /dev/null +++ b/src/main/java/com/lakala/moss/util/SSLClient.java @@ -0,0 +1,42 @@ +package com.lakala.moss.util; + +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.impl.client.DefaultHttpClient; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * @author Stephen yu + * @description: SSL处理类 + * @date 2025-02-21 + */ +public class SSLClient extends DefaultHttpClient { + public SSLClient() throws Exception { + super(); + SSLContext ctx = SSLContext.getInstance("TLS"); + X509TrustManager tm = new X509TrustManager() { + public void checkClientTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + } + + public X509Certificate[] getAcceptedIssuers() { + return null; + } + }; + ctx.init(null, new TrustManager[]{tm}, null); + SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + ClientConnectionManager ccm = this.getConnectionManager(); + SchemeRegistry sr = ccm.getSchemeRegistry(); + sr.register(new Scheme("https", 443, ssf)); + } +} diff --git a/src/main/java/com/lakala/moss/util/SecurityUtil.java b/src/main/java/com/lakala/moss/util/SecurityUtil.java new file mode 100644 index 0000000..9a79b5b --- /dev/null +++ b/src/main/java/com/lakala/moss/util/SecurityUtil.java @@ -0,0 +1,272 @@ +package com.lakala.moss.util; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang.ArrayUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.crypto.Cipher; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * @author Stephen yu + * @description: 密钥工具类 + * @date 2025-02-21 + */ +public class SecurityUtil { + private static final Logger logger = LoggerFactory.getLogger(SecurityUtil.class); + private static final String SOLT = "0123456789ABCDEF"; + private static final Provider PROVIDER = new BouncyCastleProvider(); + private static final String SIGN_VERIFY_ALGORITHM = "SHA1withRSA"; + private static final String ENCRYPTD_ECRYPT_ALGORITHM = "RSA/None/PKCS1Padding"; + private static final int RSA_KEY_LENGHTH = 1024; + + private static final String ALGORITHM = "RSA"; + + private SecurityUtil() { + throw new IllegalStateException("Utility class"); + } + + /** + * 从字符串中加载公钥 + * + * @param publicKeyStr 公钥数据字符串 + * @throws Exception 加载公钥时产生的异常 + */ + private static PublicKey loadPublicKey(String publicKeyStr) throws Exception { + try { + byte[] buffer = Base64.decodeBase64(publicKeyStr.getBytes(StandardCharsets.UTF_8)); + KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer); + return keyFactory.generatePublic(keySpec); + } catch (Exception ex) { + logger.warn("load public key exception:", ex); + throw ex; + } + } + + private static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception { + try { + byte[] buffer = Base64.decodeBase64(privateKeyStr.getBytes(StandardCharsets.UTF_8)); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer); + KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); + return keyFactory.generatePrivate(keySpec); + } catch (Exception ex) { + logger.warn("load private key exception:", ex); + throw ex; + } + } + + /** * 用公钥验证签名的正确性 * * @param message * @param signStr * @return * @throws Exception */ + public static boolean verify(String message, String signStr, String publicKeyStr) { + if (message == null || signStr == null || publicKeyStr == null) { + return false; + } + try { + PublicKey key = loadPublicKey(publicKeyStr); + Signature signetcheck = Signature.getInstance(SIGN_VERIFY_ALGORITHM); + signetcheck.initVerify(key); + signetcheck.update(message.getBytes(StandardCharsets.UTF_8)); + return signetcheck + .verify(Base64.decodeBase64(signStr.getBytes(StandardCharsets.UTF_8))); + } catch (Exception ex) { + logger.warn("verify [{}] sign [{}] exception:", message, signStr, ex); + return false; + } + + } + + public static String sign(String message, String privateKeyStr) { + try { + PrivateKey key = loadPrivateKey(privateKeyStr); + Signature signetcheck = Signature.getInstance(SIGN_VERIFY_ALGORITHM); + signetcheck.initSign(key); + signetcheck.update(message.getBytes(StandardCharsets.UTF_8)); + return new String(Base64.encodeBase64(signetcheck.sign()), StandardCharsets.UTF_8); + } catch (Exception ex) { + logger.warn("sign message [{}] exception:", message, ex); + return ""; + } + + } + + public static String encryptBase64(String messageBase64, String publicKeyStr) { + byte[] message = Base64.decodeBase64(messageBase64.getBytes(StandardCharsets.UTF_8)); + byte[] encrypted = encrypt(message, publicKeyStr); + byte[] encryptBase64 = Base64.encodeBase64(encrypted); + return new String(encryptBase64, StandardCharsets.UTF_8); + } + + public static String encryptInBytesOutBase64(byte[] message, String publicKeyStr) { + byte[] encrypted = encrypt(message, publicKeyStr); + byte[] encryptBase64 = Base64.encodeBase64(encrypted); + return new String(encryptBase64, StandardCharsets.UTF_8); + } + + public static String encryptInStringOutBase64(String message, String publicKeyStr) { + byte[] encrypted = encrypt(message.getBytes(StandardCharsets.UTF_8), publicKeyStr); + byte[] encryptBase64 = Base64.encodeBase64(encrypted); + return new String(encryptBase64, StandardCharsets.UTF_8); + } + + + public static String decryptBase64(String messageBase64, String privateKeyStr) { + byte[] message = Base64.decodeBase64(messageBase64.getBytes(StandardCharsets.UTF_8)); + byte[] decrypted = decrypt(message, privateKeyStr); + byte[] decryptBase64 = Base64.encodeBase64(decrypted); + return new String(decryptBase64, StandardCharsets.UTF_8); + } + + public static byte[] decryptInBase64OutBytes(String messageBase64, String privateKeyStr) { + byte[] message = Base64.decodeBase64(messageBase64.getBytes(StandardCharsets.UTF_8)); + return decrypt(message, privateKeyStr); + } + + public static String decryptInBase64OutString(String messageBase64, String privateKeyStr) { + byte[] message = Base64.decodeBase64(messageBase64.getBytes(StandardCharsets.UTF_8)); + return new String(decrypt(message, privateKeyStr), StandardCharsets.UTF_8); + } + + // ref: https://blog.csdn.net/taoxin52/article/details/53782470 + public static byte[] encrypt(byte[] message, String publicKeyStr) { + if (message == null || publicKeyStr == null || message.length == 0) { + return new byte[0]; + } + try { + PublicKey key = loadPublicKey(publicKeyStr); + Cipher cipher = Cipher.getInstance(ENCRYPTD_ECRYPT_ALGORITHM, PROVIDER); + cipher.init(Cipher.ENCRYPT_MODE, key); + byte[] encryptBS = null; + int plaintextLen = RSA_KEY_LENGHTH / 8 - 11; + for (int i = 0; i < message.length; i += plaintextLen) { + byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(message, i, i + plaintextLen)); + encryptBS = ArrayUtils.addAll(encryptBS, doFinal); + } + return encryptBS; + } catch (Exception ex) { + logger.info("encrypt message [{}] exception:", byteArrayToHexString(message), ex); + return new byte[0]; + } + } + + public static byte[] decrypt(byte[] message, String privateKeyStr) { + if (message == null || privateKeyStr == null || message.length == 0) { + return new byte[0]; + } + try { + PrivateKey key = loadPrivateKey(privateKeyStr); + Cipher cipher = Cipher.getInstance(ENCRYPTD_ECRYPT_ALGORITHM, PROVIDER); + cipher.init(Cipher.DECRYPT_MODE, key); + int cipherTextLen = RSA_KEY_LENGHTH / 8; + byte[] decryptBS = null; + for (int i = 0; i < message.length; i += cipherTextLen) { + byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(message, i, i + cipherTextLen)); + decryptBS = ArrayUtils.addAll(decryptBS, doFinal); + } + return decryptBS; + } catch (Exception ex) { + logger.info("decrypt message [{}] exception:", byteArrayToHexString(message), ex); + return new byte[0]; + } + } + + + + private static String byteArrayToHexString(byte[] b) { + StringBuilder sb = new StringBuilder(64); + for (int i = 0; i < b.length; i++) { + sb.append(SOLT.charAt(b[i] >>> 4 & 0x0F)); + sb.append(SOLT.charAt(b[i] & 0x0F)); + } + return sb.toString(); + + } + + + /** + * 哈希函数 + * + * @param algorithm String MD5/SHA + * @param b byte[] + * @return byte[] + */ + private static byte[] hashData(String algorithm, byte[] b) throws NoSuchAlgorithmException { + + MessageDigest md = MessageDigest.getInstance(algorithm); + md.update(b); + return md.digest(); + } + + /** + * 制作MD5 + * + * @param source String + * @return byte[] + */ + public static String md5(String source) { + return md5(source.getBytes(StandardCharsets.UTF_8)); + } + + public static String md5(byte[] b) { + try { + byte[] a = hashData("MD5", b); + return byteArrayToHexString(a); + } catch (NoSuchAlgorithmException ex) { + logger.warn("calculate [{}] md5 exception:", byteArrayToHexString(b)); + return ""; + } + } + + public static String getSignSrc(Map secMap, boolean isUseKey, String split) { + StringBuilder content = new StringBuilder(512); + List keys = new ArrayList<>(secMap.keySet()); + Collections.sort(keys); + for (int i = 0; i < keys.size(); i++) { + Object key = keys.get(i); + if ("sign".equals(key)) { + continue; + } + Object value = secMap.get(key); + if (value instanceof String) { + if (isUseKey) { + content.append((i == 0 ? "" : split) + key + "=" + (String) value); + } else { + content.append((i == 0 ? "" : split) + (String) value); + } + } + } + return content.toString(); + } + + public static String getSignSrcSkipNull(Map secMap, boolean isUseKey, + String split) { + StringBuilder content = new StringBuilder(512); + List keys = new ArrayList<>(secMap.keySet()); + Collections.sort(keys); + for (int i = 0; i < keys.size(); i++) { + Object key = keys.get(i); + if ("sign".equals(key)) { + continue; + } + Object value = secMap.get(key); + if (value instanceof String) { + if ("".equals(value)) { + continue; + } + if (isUseKey) { + content.append((i == 0 ? "" : split) + key + "=" + (String) value); + } else { + content.append((i == 0 ? "" : split) + (String) value); + } + } + } + return content.toString(); + } +} diff --git a/src/main/resources/moss-sdk.properties b/src/main/resources/moss-sdk.properties new file mode 100644 index 0000000..07907db --- /dev/null +++ b/src/main/resources/moss-sdk.properties @@ -0,0 +1,8 @@ +api.versionId = 1.0 +api.businessChannel = C00000001 +api.channelId = API +api.order.url = https://moss.wsmsd.cn/ord-api/unified/v3 + +mer.pub.key=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/g7kWVjxMV1f0CTqp4GxCwRCB6zIWOeclSvqycRUa2YjYYd92bQn+KtpogYLuzaKT0ns24wCWVar+pVx7cokG2mJsQfe4epZQcq+tBgZcIE6nSVBp9dfWMX8w6AtT3QLURqzP9tMFXrysisKbdy54kNOUPcGWC0rg11oK6BvXbwIDAQAB +mer.pri.key=MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAL+DuRZWPExXV/QJOqngbELBEIHrMhY55yVK+rJxFRrZiNhh33ZtCf4q2miBgu7NopPSezbjAJZVqv6lXHtyiQbaYmxB97h6llByr60GBlwgTqdJUGn119YxfzDoC1PdAtRGrM/20wVevKyKwpt3LniQ05Q9wZYLSuDXWgroG9dvAgMBAAECgYBh8o6A3A3uxWUYTHgSVdNIuNEmgRGWyHptWlGpXah7mPIiKLxPJylLMsONW1+JnuYdUDLwOV0dhib0IcKQ6F0nnq7CYqyPr6nL32pu0bGQbxjl6PgtYD0qc85AMHOJ5/NYujSKzW+HxByDNa6u7+m1LXDIgVm2qhiy/xYaKX58wQJBAPKPeStpZn2hzMjhmL6X0A1Xvvr+LaUgZHVOC/AA1eoGcGCPmmGHETMU1e7+ZW1Ti/JErABOqrI7bQTVHascdLECQQDKIDRJqP0CbXvJh5ThpinwpVRJ4BxTUxRs+9lCQDZ23SmFE0HhSgUZL4EMsrdPNzmdH2W56TYy3Ivpyl/SapYfAkEA5b9FuvvDiz3FFYSxQ93Rv8Gb8Gru2xgabw20uuhftaHRsXRzeusPPH4AwLWPZoUa6idnb4cToWwuL8SYrGlwkQJADtwDPA8SWqVV3mD7TwN6PdjJs4yoSG/pJoH1XOt/lYl4zfG2fCuG6G0Xnald1JMIx0ZRojNE6sRP/OYF2WBAnQJBAMIAdErHJLrKNo2ZSuJj9hrg51f540J95BjOYkXct25vX4ih3W2981gH08K+D4sk/kOLyTe1FOozJZLjjgSHfCs= +moss.pub.key=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDI/54uIovSxoDNwK+RkdXSnIwjlKPZBFcv6kYyPV9A8iyCgwcIfydXpA2ueCecyg/xPfLbFfiZpQsOUJvebtoOzAKGK9F48G7yGOG/ZhfS1ZM5LOWSVpy8sqMj8YgAhK42ZlIEivBwSdlwKkFsjDw02P57McfC0VvyVUsd/68cvwIDAQAB \ No newline at end of file diff --git a/src/test/java/com/lakala/moss/MossApiTest.java b/src/test/java/com/lakala/moss/MossApiTest.java new file mode 100644 index 0000000..6dbc714 --- /dev/null +++ b/src/test/java/com/lakala/moss/MossApiTest.java @@ -0,0 +1,173 @@ +package com.lakala.moss; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import com.lakala.moss.api.ApiReq; +import com.lakala.moss.api.ApiRes; +import com.lakala.moss.api.params.LocationInfo; +import com.lakala.moss.api.request.*; +import com.lakala.moss.api.response.*; +import com.lakala.moss.service.IMossApiService; +import com.lakala.moss.service.impl.MossApiServiceImpl; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Type; + +/** + * @author Stephen yu + * @description: 测试类 + * @date 2025-02-21 + */ +public class MossApiTest { + + private static final Logger logger = LoggerFactory.getLogger(MossApiTest.class); + + // 订单支付-拉起收银台 + @Test + public void testOrderPayCounter(){ + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + IMossApiService mossApi = new MossApiServiceImpl(); + OrderPayReq req = new OrderPayReq(); + req.setOrder_no("DemoMOSS20250225000001"); + req.setTotal_amount("1"); + req.setMer_no("M00000036"); + req.setRemark("测试"); + req.setNotify_url(""); + req.setPay_scene("0"); + req.setAccount_type("ALIPAY,WECHAT,UQRCODEPAY"); + req.setSubject("测试标题"); + req.setOrder_eff_time("300"); + req.setCallback_url("https://www.baidu.com/"); + ApiRes res = mossApi.OrderPay(req); + logger.info("res={}",gson.toJson(res)); + } + // 订单支付-独立支付 + @Test + public void testOrderPay(){ + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + IMossApiService mossApi = new MossApiServiceImpl(); + OrderPayReq req = new OrderPayReq(); + req.setOrder_no("DemoMOSS202502250000012"); + req.setTotal_amount("1"); + req.setMer_no("M00000036"); + req.setRemark("测试"); + req.setNotify_url(""); + req.setPay_scene("1"); + req.setAccount_type("UQRCODEPAY"); + req.setSubject("测试标题"); + req.setOrder_eff_time("15"); + req.setTrans_type("51"); + req.setUser_id("olpr-0sYaDrxaT5GmLUgUenVcDPo"); + LocationInfo location_info = new LocationInfo(); + location_info.setRequest_ip("36.45.36.95"); + location_info.setBase_station("00 6361 58130"); + location_info.setLocation("+37.123456789,-121.123456789"); + req.setLocation_info(location_info); + ApiRes res = mossApi.OrderPay(req); + logger.info("res={}",gson.toJson(res)); + } + // 订单查询 + @Test + public void testOrderQryList(){ + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + IMossApiService mossApi = new MossApiServiceImpl(); + OrderQryListReq req = new OrderQryListReq(); + req.setOrder_no("DemoMOSS20250225000001"); + req.setPay_serial("0bf31ad8e73e4e548960599b70d9b591"); + ApiRes res = mossApi.OrderQryList(req); + logger.info("res={}",gson.toJson(res)); + } + // 订单关单 + @Test + public void testOrderCls(){ + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + IMossApiService mossApi = new MossApiServiceImpl(); + OrderClsReq req = new OrderClsReq(); + req.setOrigin_order_no("DemoMOSS20250225000001"); + LocationInfo location_info = new LocationInfo(); + location_info.setRequest_ip("36.45.36.95"); + location_info.setBase_station("00 6361 58130"); + location_info.setLocation("+37.123456789,-121.123456789"); + req.setLocation_info(location_info); + ApiRes res = mossApi.OrderCls(req); + logger.info("res={}",gson.toJson(res)); + } + // 订单退款 + @Test + public void testOrderRefund(){ + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + IMossApiService mossApi = new MossApiServiceImpl(); + OrderRefundReq req = new OrderRefundReq(); + req.setOrder_no("RefundMOSS20250225000001"); + req.setOrigin_order_no("DemoMOSS20250225000001"); + req.setOrigin_pay_serial("0bf31ad8e73e4e548960599b70d9b591"); + req.setRefund_amount("1"); + req.setRefund_reason("测试退款"); + LocationInfo location_info = new LocationInfo(); + location_info.setRequest_ip("36.45.36.95"); + location_info.setBase_station("00 6361 58130"); + location_info.setLocation("+37.123456789,-121.123456789"); + req.setLocation_info(location_info); + ApiRes res = mossApi.OrderRefund(req); + logger.info("res={}",gson.toJson(res)); + } + // 订单查询-退款 + @Test + public void testOrderQryRefund(){ + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + IMossApiService mossApi = new MossApiServiceImpl(); + OrderQryListReq req = new OrderQryListReq(); + req.setOrder_no("RefundMOSS20250225000001"); + ApiRes res = mossApi.OrderQryList(req); + logger.info("res={}",gson.toJson(res)); + } + // 订单通知 + @Test + public void testOrderNotice(){ + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + Type type = new TypeToken>() {}.getType(); + IMossApiService mossApi = new MossApiServiceImpl(); + String request = "{\"head\":{\"requestTime\":\"20250227161640\",\"versionId\":\"1.0\",\"org\":\"000000000001\",\"serviceSn\":\"94452612AAB545149ED020431DE5C5F3\",\"serviceId\":\"lfops.moss.order.ntc\",\"channelId\":\"01\",\"businessChannel\":\"C00000001\",\"sid\":\"94452612AAB545149ED020431DE5C5F3\",\"accessToken\":\"\"},\"sign\":\"iZu2qma07bCjelm5jMuhtoXXG5/uqPSE/TgCGU6YerVpt7fwIqX9Y+wBTdZr8EXZF4CiU79usuVOi18SxvbXORXMsDUUlLL8E5eIvjh/xuaiYC/IXeOMC1CM+IbD9ql9n4/2E3f/wn5+OOtOgZM/HeVME7lsyndGvx6Iqof+kn0=\",\"requestEncrypted\":\"l+ULAxGYu3FFUIxAvY5h8f3qMoolXISOzdKp80zR2Jc016IQtVfKikWq6BPAaqzzLgL4rIWrQfr316iQVRQcN3OhIQS7ViRseeE7S1NVXSPWhypVWwnas6tfiURYqm+yydIiQ0e0FvLPIR6Hf909d3QdiDWoRQFXyzIVwjTnPbARk01NccPOLk/0HtEqoIPbmI/hOQYoxzY22Dj+WetAcRNdzaOL8vbFByTpHYSKu2teg+irEKLbs4VYpi0husKAT6eqyrX8ZTxMCeGhwsiLku68BOz7ddeA7aV/JJDOXDtd/qGjOhQIZL8anMbaN9CGY3xodCweGXRcYInc6frV0DhHu7FKO60//Q6zMwVwPp4qcA9mLpvclC37uE3CD5JJaOdyfVV2f4NgFa5lnFBYRXIicWTTQlPaNgZw4n9j61M7eWhFFzPqwvcGxcYxCcYrVmVZNr/KGHV4hGWuvjI9tiPJ1Uu5ZpHwkL75H+921uJLDmoshmD6VlgfPW4mmXY1QeeUTnQVHiIRN3BQ/c4ZjiAcxLEpQV4a26Vh5PDviQM0C6kZdLSGFmbMsMawcHV7LfzMbi2PbGGwWs/+sXKbb6m9bW1yH7sUsT9ScA0oJCDeRtpAO4yfnejU4b9Jj11vD9Wi7hilF1SMKAHKl3zzzmtRaN74oAYDCz5Zuo9bqZxJ/0ktBFfnIZ5aCUAiW6IFeaRsmQ2jXTO7JbDSfX5LfgA0lE1F7OV/Q7KC4TaVyz2XQ3FNztB9xDPY2uaNdetRIYk+71/cXmYcUIJPeqt0FcP7DXDzAKBkWbZh7MvC/7pDwBm6oW06HKDWiK2qJjRYi8k9Xzo+Q2T0nBpLLdMVKIDmEo1hJm13PC8ovl68kSvXEMSqs0D6snXxI+Op9QvkVjr3Q6fEbl3gKofPnssTzkmYLqK5hQ4AamiMNh5tC7fyd9T+w88iz6uuxQ/WcJ/q+FA6nLcjbkAouWiubU1ZCdYux9QkRaG4iZUJZ0Vo7b/7s1yg86+GjoJ2nsETU8fX\"}"; + ApiReq req = gson.fromJson(request, type); + ApiRes res = mossApi.OrderNotice(req); + logger.info("res={}",gson.toJson(res)); + } + // 申请对账单 + @Test + public void testOrderChkApply(){ + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + IMossApiService mossApi = new MossApiServiceImpl(); + OrderChkApplyReq req = new OrderChkApplyReq(); + req.setTran_date("20250225"); + req.setApply_order_no("AONMOSS20250226000003"); + ApiRes res = mossApi.OrderChkApply(req); + logger.info("res={}",gson.toJson(res)); + } + // 用户标识查询 + @Test + public void testUseridQry(){ + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + IMossApiService mossApi = new MossApiServiceImpl(); + UseridQryReq req = new UseridQryReq(); + req.setMer_no("M00000036"); + req.setAuth_code("5oqA5pyv5pSv5oyB5Zui6Zif6Ze16Z2S"); + req.setApp_up_identifier("UnionPay/1.0 ICBCeLife"); + ApiRes res = mossApi.UseridQry(req); + logger.info("res={}",gson.toJson(res)); + } + // 对账单下载 + @Test + public void testChkDownload(){ + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + IMossApiService mossApi = new MossApiServiceImpl(); + String downUrl = "https://jrt.wsmsd.cn/lfpf-sch/chkfile/download?apply_order_no=AONMOSS20250226000003&file_name=F20250225_TRAN_CHECK_C00000001_01.xls"; + String localDir = "d:/localpath/data/"; + String localFileName = "F20250225_TRAN_CHECK_C00000001_01.xls"; + boolean res = mossApi.ChkDownload(downUrl,localDir,localFileName); + logger.info("res={}",res); + } + +}