|
|
|
@ -2,17 +2,24 @@ package com.ynxbd.jy.helper; |
|
|
|
|
|
|
|
|
|
|
|
import com.alibaba.fastjson.JSONObject; |
|
|
|
import com.alibaba.fastjson.JSONObject; |
|
|
|
import com.ynxbd.common.bean.enums.MerchantEnum; |
|
|
|
import com.ynxbd.common.bean.enums.MerchantEnum; |
|
|
|
|
|
|
|
import com.ynxbd.common.bean.pay.ReqOrder; |
|
|
|
import com.ynxbd.common.helper.common.ErrorHelper; |
|
|
|
import com.ynxbd.common.helper.common.ErrorHelper; |
|
|
|
import com.ynxbd.common.helper.common.JsonHelper; |
|
|
|
import com.ynxbd.common.helper.common.JsonHelper; |
|
|
|
|
|
|
|
import com.ynxbd.common.helper.common.RSAHelper; |
|
|
|
|
|
|
|
import com.ynxbd.common.helper.http.OkHttpHelper; |
|
|
|
|
|
|
|
import com.ynxbd.jy.bean.JYOrder; |
|
|
|
import com.ynxbd.wx.config.WeChatConfig; |
|
|
|
import com.ynxbd.wx.config.WeChatConfig; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import org.apache.commons.lang3.ObjectUtils; |
|
|
|
import org.apache.commons.lang3.ObjectUtils; |
|
|
|
import weixin.popular.api.PayMchAPI; |
|
|
|
|
|
|
|
import weixin.popular.bean.paymch.Unifiedorder; |
|
|
|
|
|
|
|
import weixin.popular.bean.paymch.UnifiedorderResult; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import java.math.BigDecimal; |
|
|
|
import java.math.BigDecimal; |
|
|
|
import java.util.UUID; |
|
|
|
import java.nio.charset.StandardCharsets; |
|
|
|
|
|
|
|
import java.security.PrivateKey; |
|
|
|
|
|
|
|
import java.security.Signature; |
|
|
|
|
|
|
|
import java.util.Base64; |
|
|
|
|
|
|
|
import java.util.HashMap; |
|
|
|
|
|
|
|
import java.util.Map; |
|
|
|
|
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
|
|
|
|
|
|
|
// 京颐支付
|
|
|
|
// 京颐支付
|
|
|
|
@Slf4j |
|
|
|
@Slf4j |
|
|
|
@ -31,70 +38,284 @@ public class JYPayHelper { |
|
|
|
* @param msgOpenId 用于推送的openId |
|
|
|
* @param msgOpenId 用于推送的openId |
|
|
|
* @return 成功返回 json, 否则返回null |
|
|
|
* @return 成功返回 json, 否则返回null |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static JSONObject createOrder(MerchantEnum merchantEnum, String title, String totalFee, String outTradeNo, String notifyType, String ip, String payOpenId, String msgOpenId) { |
|
|
|
public static JSONObject createOrder(MerchantEnum merchantEnum, String title, String totalFee, String outTradeNo, String notifyType, String ip, String payOpenId, String msgOpenId, ReqOrder reqOrder) { |
|
|
|
// try {
|
|
|
|
try { |
|
|
|
// if (ObjectUtils.isEmpty(payOpenId) || ObjectUtils.isEmpty(outTradeNo)
|
|
|
|
if (ObjectUtils.isEmpty(payOpenId) || ObjectUtils.isEmpty(outTradeNo) |
|
|
|
// || merchantEnum == null || totalFee == null || notifyType == null) {
|
|
|
|
|| merchantEnum == null || totalFee == null || notifyType == null) { |
|
|
|
// log.info("【京硕】下单参数缺失");
|
|
|
|
log.info("【京硕】下单参数缺失"); |
|
|
|
// return null;
|
|
|
|
return null; |
|
|
|
// }
|
|
|
|
} |
|
|
|
//
|
|
|
|
|
|
|
|
// String cents;
|
|
|
|
String cents; |
|
|
|
// try {
|
|
|
|
try { |
|
|
|
// cents = new BigDecimal(totalFee).movePointRight(2).toString(); // 金额转换=>单位分
|
|
|
|
cents = new BigDecimal(totalFee).movePointRight(2).toString(); // 金额转换=>单位分
|
|
|
|
// } catch (Exception e) {
|
|
|
|
} catch (Exception e) { |
|
|
|
// ErrorHelper.println(e);
|
|
|
|
ErrorHelper.println(e); |
|
|
|
// log.info("【京硕】统一下单金额转换异常");
|
|
|
|
log.info("【京硕】下单金额转换异常"); |
|
|
|
// return null;
|
|
|
|
return null; |
|
|
|
// }
|
|
|
|
} |
|
|
|
//
|
|
|
|
|
|
|
|
// String appId = merchantEnum.APP_ID;
|
|
|
|
log.info("【京硕】下单 {},{},{},{},{}", title, outTradeNo, totalFee, ip, merchantEnum.NOTIFY_URL); |
|
|
|
// String mchId = WeChatConfig.MCH_ID;
|
|
|
|
|
|
|
|
// String mchKey = WeChatConfig.MCH_KEY;
|
|
|
|
JYOrder jyOrder = reqOrder.jyOrder; |
|
|
|
// String nonce = UUID.randomUUID().toString().replace("-", "");
|
|
|
|
|
|
|
|
//
|
|
|
|
Map<String, String> req = new HashMap<>(); |
|
|
|
// Unifiedorder unifiedorder = new Unifiedorder();
|
|
|
|
req.put("openId", payOpenId); |
|
|
|
// unifiedorder.setAppid(appId);
|
|
|
|
req.put("hospitalId", JYPayConfig.HOSP_ID); // 医院ID
|
|
|
|
// unifiedorder.setMch_id(mchId);
|
|
|
|
req.put("applicationId", JYPayConfig.APPLICATION_ID); |
|
|
|
// unifiedorder.setNonce_str(nonce);
|
|
|
|
req.put("merchantId", JYPayConfig.MERCHANT_ID); |
|
|
|
// unifiedorder.setOpenid(payOpenId);
|
|
|
|
req.put("merOrderNum", outTradeNo); // 商户订单号
|
|
|
|
// unifiedorder.setOut_trade_no(outTradeNo);
|
|
|
|
req.put("hisOrderNum", null); // HIS订单号:可以为发票号[该接口若不传,在业务完成后调用2.1.6接口]
|
|
|
|
// unifiedorder.setBody(title);
|
|
|
|
req.put("hisTransNum", null); // HIS流水号:可以为发票号[该接口若不传,在业务完成后调用2.1.6接口]
|
|
|
|
//
|
|
|
|
// 金额
|
|
|
|
// unifiedorder.setTotal_fee(cents); // 金额分
|
|
|
|
req.put("orderAmt", cents); // 交易金额:只接收整数,单位:分,例如100表示壹圆整;支付宝医保支付传总金额
|
|
|
|
// unifiedorder.setSpbill_create_ip(ip);
|
|
|
|
|
|
|
|
// unifiedorder.setNotify_url(WeChatConfig.getBaseURL() + merchantEnum.NOTIFY_URL);
|
|
|
|
req.put("orderType", jyOrder.orderTypeEnum.CODE); // 如:001:挂号费 002:医药费;
|
|
|
|
// unifiedorder.setTrade_type("JSAPI");
|
|
|
|
req.put("backURL", WeChatConfig.getBaseURL() + merchantEnum.NOTIFY_URL); // 商户异步回调地址:后台发起异步回调地址, 扫码枪支付方式无需回调的可以不
|
|
|
|
// // 额外参数
|
|
|
|
// params.put("backParamsType", "key-value"); // 商户异步回调入参格式:后台发起异步回调时参数格式,取值有:key-value和json,默认值是key-value。
|
|
|
|
// unifiedorder.setAttach(notifyType);
|
|
|
|
req.put("payType", "WX_PUBLIC"); // 如:UPOP_APP, UPOP_WEB;详见附录5.1[微信公众号支付(WX_PUBLIC)]
|
|
|
|
//
|
|
|
|
req.put("bussinessType", jyOrder.businessTypeEnum.CODE); // 业务类型:综合支付对应用权限控制是否必传 最大不超过4位,详见数据字典5.2 该接口若不传,需要调用2.1.6接口再次更新
|
|
|
|
// log.info("【京硕】统一下单 {},{},{},{},{},{},{},{}", appId, mchId, nonce, title, outTradeNo, totalFee, ip, merchantEnum.NOTIFY_URL);
|
|
|
|
req.put("patientName", reqOrder.getPatientName()); // 微信医保支付必传。其余支付方式若该接口若不传,需要调用2.1.6接口再次更新
|
|
|
|
//
|
|
|
|
req.put("patientGender", reqOrder.getPatientGender()); // 患者性别。综合支付对应用权限控制是否必传 1男 2女 9其他 最大不超过4位 该接口若不传,需要调用2.1.6接口再次更新
|
|
|
|
// // 微信统一下单
|
|
|
|
req.put("patientAge", reqOrder.getPatientAge()); // 患者年龄。综合支付对应用权限控制是否必传 该接口若不传,需要调用2.1.6接口再次更新
|
|
|
|
// UnifiedorderResult unifiedorderResult = PayMchAPI.payUnifiedorder(unifiedorder, mchKey);
|
|
|
|
req.put("patientIdNo", reqOrder.getPatientTel()); // 微信医保支付必传。其余支付方式若该接口若不传,需要调用2.1.6接口再次更新
|
|
|
|
// log.info("【京硕】统一下单返回:{}", JsonHelper.toJsonString(unifiedorderResult));
|
|
|
|
req.put("patientPhoneNumber", reqOrder.getPatientTel()); // 综合支付对应用权限控制是否必传 最大不超过16位 该接口若不传,需要调用2.1.6接口再次更新
|
|
|
|
// if (unifiedorderResult == null) {
|
|
|
|
req.put("patientId", reqOrder.getPatientId()); // 患者ID:综合支付对应用权限控制是否必传 最大不超过32位 该接口若不传,需要调用2.1.6接口再次更新
|
|
|
|
// log.info("【京硕】统一下单失败");
|
|
|
|
req.put("patientCardNo", reqOrder.getPatientId()); // 患者卡号:综合支付对应用权限控制是否必传 最大不超过32位该接口若不传,需要调用2.1.6接口再次更新
|
|
|
|
// return null;
|
|
|
|
req.put("clinicNo", reqOrder.getTreatNum()); // 门诊号:综合支付对应用权限控制是否必传 最大不超过32位该接口若不传,需要调用2.1.6接口再次更新
|
|
|
|
// }
|
|
|
|
req.put("inpNo", reqOrder.getTreatNum()); // 住院号:综合支付对应用权限控制是否必传 最大不超过32位该接口若不传,需要调用2.1.6接口再次更新
|
|
|
|
|
|
|
|
req.put("hisTransDoneTime", null); // HIS业务完成时间:综合支付对应用权限控制是否必传 YYYY-MM-DD HH:MM:SS 该接口若不传,需要调用2.1.6接口再次更新
|
|
|
|
|
|
|
|
req.put("hisOperatorNo", jyOrder.getHisOperatorNo()); // HIS操作员编号:综合支付对应用权限控制是否必传HIS操作员编号和HIS操作员姓名必须成对出现该接口若不传,需要调用2.1.6接口再次更新
|
|
|
|
|
|
|
|
req.put("hisOperatorName", jyOrder.getHisOperatorName()); // HIS操作员姓名:综合支付对应用权限控制是否必传 HIS操作员编号和HIS操作员姓名必须成对出现 该接口若不传,需要调用2.1.6接口再次更新
|
|
|
|
|
|
|
|
req.put("receiptNo", null); // 综合支付对应用权限控制是否必传最大不超过200位 该接口若不传,需要调用2.1.6接口再次更新
|
|
|
|
|
|
|
|
req.put("remarks", ""); // 备注 建行渠道格式为json,参考样式:{"REMARK1":"REM1","REMARK2":"REM2"} 农行渠道参考样式:{"TerminalNo":"1234567"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 医保支付======================================
|
|
|
|
|
|
|
|
// params.put("orderTotalAmt", cents); // orderTotalAmt:微信医保支付必传,有社保交易数据时,订单总金额=自费实际金额+社保金额+减免金额
|
|
|
|
|
|
|
|
// params.put("orderTypeDetail", jyOrder.orderTypeDetailEnum.CODE); // 支付类型明细:由接入方生成的签名,支付平台根据该字段对入参进行验签;签名生成规则参考4加签验签规则
|
|
|
|
|
|
|
|
// params.put("cashFee", reqOrder.getCashFee()); // 现金需要支付的金额:单位为分>=0 线下预结算模式填实际自费金额
|
|
|
|
|
|
|
|
// params.put("insuranceFee", reqOrder.getInsuranceFee()); // 单位为分>=0填实际医保金额
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// String result_code = unifiedorderResult.getResult_code();
|
|
|
|
// params.put("allowFeeChange", "0"); // 是否允许预结算费用发生变化:0:不允许;1:允许 当医保局返回的预结算金额与医院上传的金额不一致时,此字段为0则直接报错,为1则以医保局金额为准
|
|
|
|
// // 下单是否成功
|
|
|
|
// params.put("spbillCreateIp", reqOrder.getIp()); // 用户端ip
|
|
|
|
// Boolean sign_status = unifiedorderResult.getSign_status();
|
|
|
|
// params.put("returnUrl", reqOrder.getReturnUrl()); // 调用方可以在链接中加上支付单号,回跳后查询调用方订单状态,如果订单已支付则成功, 如果订单未支付则进行查单操作确认订单结果。不论成功或者失败均会回跳
|
|
|
|
// if (!SUCCESS.equals(result_code) || sign_status == null || !sign_status) {
|
|
|
|
// params.put("insurancePayType", jyOrder.toInsurancePayType()); // 微信医保支付方式:1:现金;2:医保;3:现金+医保
|
|
|
|
// log.info("【京硕】统一下单返回异常信息:sign_status={}, err_code=[ {} ], err_code_des=[ {} ]", sign_status, unifiedorderResult.getErr_code(), unifiedorderResult.getErr_code_des());
|
|
|
|
// params.put("consumeType", jyOrder.toConsumeType()); // 医保部分扣费类型:0:统筹+个账;1:个账;2:统筹。默认为0
|
|
|
|
// return null;
|
|
|
|
// params.put("userCardType", "1"); // 1 居民身份证
|
|
|
|
// }
|
|
|
|
// params.put("isDept", "4"); // 医保标识:is_dept=4 医保电子凭证支付;
|
|
|
|
// String json = weixin.popular.util.PayUtil.generateMchPayJsRequestJson(unifiedorderResult.getPrepay_id(), appId, mchKey);
|
|
|
|
// params.put("serialNo", reqOrder.getSerialNo()); // 医院HIS系统订单号
|
|
|
|
// if (json != null) {
|
|
|
|
// params.put("requestContent", reqOrder.getRequestContent()); // 参考医保结构体(医疗机构透传医保) 示例: {"payAuthNo":"AUTH****","payOrdId":"ORD****","setlLatlnt":"118.096435,24.485407"} payAuthNo:授权码 payOrdId:对应处方上传的出参单号 setlLatlnt:用户地理定位信息, 经纬度信息,如“118.096435,24.485407”。未获取到用户定位时,传“0,0”。
|
|
|
|
// return JsonHelper.parseObject(json);
|
|
|
|
//// params.put("extendInfo", ""); //亲情移动支付时,为必填。 (就诊亲属姓名md5 - rel_user_name_md5,就诊亲属身份证件号md5 - rel_user_card_no_md5 ) { "rel_user_name_md5": "XXX","rel_user_card_no_md5": "XXX"} MD5为32位小写,uft-8编码(身份证中如果有字母需要转成大写再做MD5)
|
|
|
|
// }
|
|
|
|
|
|
|
|
// } catch (Exception e) {
|
|
|
|
String signValue = createSign(req); |
|
|
|
// log.error("【京硕】统一下单异常信息:{}", e.getMessage());
|
|
|
|
if (signValue == null) { |
|
|
|
// ErrorHelper.println(e);
|
|
|
|
return null; |
|
|
|
// }
|
|
|
|
} |
|
|
|
// return null;
|
|
|
|
req.put("signValue", signValue); // 数字签名:由接入方生成的签名,支付平台根据该字段对入参进行验签;签名生成规则参考4加签验签规则
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JSONObject resp = OkHttpHelper.postJson(JYPayConfig.URL + "/qy_iips_pay/paycore/prePayOrder", params -> { |
|
|
|
|
|
|
|
params.putAll(req); |
|
|
|
|
|
|
|
}, headers -> { |
|
|
|
|
|
|
|
headers.add("content-type", "application/x-www-form-urlencoded"); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
log.info("【京硕】下单返回 {}", JsonHelper.toJsonString(resp)); |
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
|
|
log.error("【京硕】下单异常信息:", e); |
|
|
|
|
|
|
|
} |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 支付状态查询 |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param merchantEnum 枚举 |
|
|
|
|
|
|
|
* @param title 商品标题 |
|
|
|
|
|
|
|
* @param totalFee 订单金额 |
|
|
|
|
|
|
|
* @param outTradeNo 订单号 |
|
|
|
|
|
|
|
* @return 成功返回 json, 否则返回null |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public static JSONObject queryPayOrderInfo(MerchantEnum merchantEnum, String title, String totalFee, String outTradeNo, String bankTransNo, String tradeNo) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
log.info("【京硕】支付状态查询 {},{},{},{}", title, outTradeNo, totalFee, merchantEnum.NOTIFY_URL); |
|
|
|
|
|
|
|
if (ObjectUtils.isEmpty(outTradeNo) && ObjectUtils.isEmpty(bankTransNo) && ObjectUtils.isEmpty(tradeNo)) { |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (ObjectUtils.isEmpty(outTradeNo) && (ObjectUtils.isEmpty(bankTransNo) || ObjectUtils.isEmpty(tradeNo))) { |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Map<String, String> req = new HashMap<>(); |
|
|
|
|
|
|
|
req.put("applicationId", JYPayConfig.APPLICATION_ID); |
|
|
|
|
|
|
|
req.put("merchantId", JYPayConfig.MERCHANT_ID); |
|
|
|
|
|
|
|
req.put("merOrderNum", outTradeNo); // 商户订单号
|
|
|
|
|
|
|
|
req.put("orderId", bankTransNo); // 支付平台订单号
|
|
|
|
|
|
|
|
req.put("outTransNo", tradeNo); // 应用流水号:应用流水号outTransNo”和“支付平台订单号orderId”这两个参数必须同时传。该参数指“应用方流水号”,不许有重复
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String signValue = createSign(req); |
|
|
|
|
|
|
|
if (signValue == null) { |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
req.put("signValue", signValue); // 应用流水号:应用流水号outTransNo”和“支付平台订单号orderId”这两个参数必须同时传。该参数指“应用方流水号”,不许有重复
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JSONObject resp = OkHttpHelper.postJson(JYPayConfig.URL + "/qy_iips_pay/paycore/queryPayOrderInfo", params -> { |
|
|
|
|
|
|
|
params.putAll(req); |
|
|
|
|
|
|
|
}, headers -> { |
|
|
|
|
|
|
|
headers.add("content-type", "application/x-www-form-urlencoded"); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
log.info("【京硕】支付状态查询返回 {}", JsonHelper.toJsonString(resp)); |
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
|
|
log.error("【京硕】支付状态查询异常信息:", e); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 退款申请 |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param merchantEnum 枚举 |
|
|
|
|
|
|
|
* @param title 商品标题 |
|
|
|
|
|
|
|
* @param totalFee 订单金额 |
|
|
|
|
|
|
|
* @param outTradeNo 订单号 |
|
|
|
|
|
|
|
* @return 成功返回 json, 否则返回null |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public static JSONObject refundPayOrder(MerchantEnum merchantEnum, String title, String totalFee, String outTradeNo, String bankTransNo, String tradeNo, String sign) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
log.info("【京硕】退款申请 {},{},{},{}", title, outTradeNo, totalFee, merchantEnum.NOTIFY_URL); |
|
|
|
|
|
|
|
if (ObjectUtils.isEmpty(outTradeNo) && ObjectUtils.isEmpty(bankTransNo) && ObjectUtils.isEmpty(tradeNo)) { |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (ObjectUtils.isEmpty(outTradeNo) && (ObjectUtils.isEmpty(bankTransNo) || ObjectUtils.isEmpty(tradeNo))) { |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Map<String, String> req = new HashMap<>(); |
|
|
|
|
|
|
|
req.put("applicationId", JYPayConfig.APPLICATION_ID); |
|
|
|
|
|
|
|
req.put("merchantId", JYPayConfig.MERCHANT_ID); |
|
|
|
|
|
|
|
req.put("merOrderNum", outTradeNo); // 商户订单号
|
|
|
|
|
|
|
|
req.put("orderId", bankTransNo); // 支付平台订单号
|
|
|
|
|
|
|
|
req.put("outTransNo", tradeNo); // 应用流水号:应用流水号outTransNo”和“支付平台订单号orderId”这两个参数必须同时传。该参数指“应用方流水号”,不许有重复
|
|
|
|
|
|
|
|
req.put("signValue", sign); // 应用流水号:应用流水号outTransNo”和“支付平台订单号orderId”这两个参数必须同时传。该参数指“应用方流水号”,不许有重复
|
|
|
|
|
|
|
|
req.put("refundStateSync", "0"); // 退费状态是否同步返回[0:否,1:是,不传默认为否 若传值为1,则退款状态不明确,开始轮询]
|
|
|
|
|
|
|
|
req.put("requestContent", "0"); // 微信医保支付必传。示例:{“payOrdId”:”******”,“ref_reason”:”******”ref_reason 退款原因payOrdId: 对应处方上传的出参单号
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String signValue = createSign(req); |
|
|
|
|
|
|
|
if (signValue == null) { |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
req.put("signValue", signValue); // 数字签名:由接入方生成的签名,支付平台根据该字段对入参进行验签;签名生成规则参考4加签验签规则
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JSONObject resp = OkHttpHelper.postJson(JYPayConfig.URL + "/qy_iips_pay/paycore/refundPayOrder", params -> { |
|
|
|
|
|
|
|
params.putAll(req); |
|
|
|
|
|
|
|
}, headers -> { |
|
|
|
|
|
|
|
headers.add("content-type", "application/x-www-form-urlencoded"); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
log.info("【京硕】退款申请返回 {}", JsonHelper.toJsonString(resp)); |
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
|
|
log.error("【京硕】退款申请异常信息:", e); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 更新业务信息 |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param merchantEnum 枚举 |
|
|
|
|
|
|
|
* @param title 商品标题 |
|
|
|
|
|
|
|
* @param totalFee 订单金额 |
|
|
|
|
|
|
|
* @param outTradeNo 订单号 |
|
|
|
|
|
|
|
* @return 成功返回 json, 否则返回null |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public static JSONObject modifyBusinessInfo(MerchantEnum merchantEnum, String title, String totalFee, String outTradeNo, String bankTransNo, String tradeNo, String sign) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
log.info("【京硕】更新业务信息 {},{},{},{}", title, outTradeNo, totalFee, merchantEnum.NOTIFY_URL); |
|
|
|
|
|
|
|
if (ObjectUtils.isEmpty(outTradeNo) && ObjectUtils.isEmpty(bankTransNo) && ObjectUtils.isEmpty(tradeNo)) { |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (ObjectUtils.isEmpty(outTradeNo) && (ObjectUtils.isEmpty(bankTransNo) || ObjectUtils.isEmpty(tradeNo))) { |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JSONObject resp = OkHttpHelper.postJson(JYPayConfig.URL + "/qy_iips_pay/paycore/modifyBusinessInfo", params -> { |
|
|
|
|
|
|
|
params.put("applicationId", JYPayConfig.APPLICATION_ID); // 应用编号
|
|
|
|
|
|
|
|
params.put("merchantId", JYPayConfig.MERCHANT_ID); // 应用商户编号
|
|
|
|
|
|
|
|
// params.put("merOrderNum", merOrderNum); // 商户订单号
|
|
|
|
|
|
|
|
// params.put("refundTransId", refundTransId); // 商户退款流水号,非支付交易必传
|
|
|
|
|
|
|
|
// params.put("orderId", orderId); // 支付平台订单号
|
|
|
|
|
|
|
|
// params.put("orderTransId", orderTransId); // 支付平台流水号
|
|
|
|
|
|
|
|
// params.put("hisOrderNum", hisOrderNum); // HIS订单号,对账场景必传
|
|
|
|
|
|
|
|
// params.put("hisTransNum", hisTransNum); // HIS交易流水号,对账场景必传
|
|
|
|
|
|
|
|
// params.put("hisTransAmt", hisTransAmt); // 交易金额,单位:分,整数
|
|
|
|
|
|
|
|
// params.put("bussinessType", bussinessType); // 业务类型,最大4位
|
|
|
|
|
|
|
|
// params.put("patientName", patientName); // 患者姓名
|
|
|
|
|
|
|
|
// params.put("patientGender", patientGender); // 患者性别:1男 2女 9其他
|
|
|
|
|
|
|
|
// params.put("patientAge", patientAge); // 患者年龄
|
|
|
|
|
|
|
|
// params.put("patientIdNo", patientIdNo); // 患者身份证号,最长18位
|
|
|
|
|
|
|
|
// params.put("patientPhoneNumber", patientPhoneNumber); // 患者手机号码,最长16位
|
|
|
|
|
|
|
|
// params.put("patientId", patientId); // 患者ID
|
|
|
|
|
|
|
|
// params.put("patientCardNo", patientCardNo); // 患者卡号
|
|
|
|
|
|
|
|
// params.put("clinicNo", clinicNo); // 门诊号
|
|
|
|
|
|
|
|
// params.put("inpNo", inpNo); // 住院号
|
|
|
|
|
|
|
|
// params.put("hisTransDoneTime", hisTransDoneTime); // HIS业务完成时间 YYYY-MM-DD HH:MM:SS
|
|
|
|
|
|
|
|
// params.put("hisOperatorNo", hisOperatorNo); // HIS操作员编号
|
|
|
|
|
|
|
|
// params.put("hisOperatorName", hisOperatorName); // HIS操作员姓名
|
|
|
|
|
|
|
|
// params.put("receiptNo", receiptNo); // HIS票据号
|
|
|
|
|
|
|
|
// params.put("hospitalId", hospitalId); // 医院ID
|
|
|
|
|
|
|
|
// params.put("iipsApiVersion", iipsApiVersion); // API版本号,不传默认v1.0
|
|
|
|
|
|
|
|
}, headers -> { |
|
|
|
|
|
|
|
headers.add("content-type", "application/x-www-form-urlencoded"); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
log.info("【京硕】更新业务信息返回 {}", JsonHelper.toJsonString(resp)); |
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
|
|
log.error("【京硕】更新业务信息异常信息:", e); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 【可选】排除signValue自身(标准支付签名规范推荐) |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param params 原始参数map |
|
|
|
|
|
|
|
* @return 待签名字符串 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public static String createSign(Map<String, String> params) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
String signString = buildSignString(params); |
|
|
|
|
|
|
|
return sign(signString, RSAHelper.getPrivateKey(JYPayConfig.PRIVATE_KEY)); |
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
|
|
log.error("", e); |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 【可选】排除signValue自身(标准支付签名规范推荐) |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param params 原始参数map |
|
|
|
|
|
|
|
* @return 待签名字符串 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public static String buildSignString(Map<String, String> params) { |
|
|
|
|
|
|
|
return params.entrySet().stream().filter(entry -> entry.getValue() != null && !entry.getValue().trim().isEmpty() && !"signValue".equals(entry.getKey())) |
|
|
|
|
|
|
|
.sorted(Map.Entry.comparingByKey()) |
|
|
|
|
|
|
|
.map(entry -> entry.getKey() + "=" + entry.getValue()) |
|
|
|
|
|
|
|
.collect(Collectors.joining("&")); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 使用私钥按照指定算法进行签名 |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param message 待签名串 |
|
|
|
|
|
|
|
* @param privateKey 签名用私钥对象 |
|
|
|
|
|
|
|
* @return 签名结果 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public static String sign(String message, PrivateKey privateKey) throws Exception { |
|
|
|
|
|
|
|
Signature signature = Signature.getInstance("SHA256WithRSA"); |
|
|
|
|
|
|
|
signature.initSign(privateKey); |
|
|
|
|
|
|
|
signature.update(message.getBytes(StandardCharsets.UTF_8)); |
|
|
|
|
|
|
|
return Base64.getEncoder().encodeToString(signature.sign()); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|