提交
This commit is contained in:
@ -1,11 +1,13 @@
|
||||
package co.yixiang.yshop.module.pay.config.handlers;
|
||||
|
||||
import co.yixiang.yshop.module.pay.enums.PayTypeEnum;
|
||||
import co.yixiang.yshop.module.pay.mq.producer.PayNoticeProducer;
|
||||
import com.egzosn.pay.ali.api.AliPayService;
|
||||
import com.egzosn.pay.ali.bean.AliPayMessage;
|
||||
import com.egzosn.pay.common.api.PayMessageHandler;
|
||||
import com.egzosn.pay.common.bean.PayOutMessage;
|
||||
import com.egzosn.pay.common.exception.PayErrorException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@ -16,6 +18,7 @@ import java.util.Map;
|
||||
* @author hupeng
|
||||
* @date 2023/7/15
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class AliPayMessageHandler implements PayMessageHandler<AliPayMessage, AliPayService> {
|
||||
|
||||
@ -41,9 +44,10 @@ public class AliPayMessageHandler implements PayMessageHandler<AliPayMessage, Al
|
||||
if ("TRADE_SUCCESS".equals(trade_status) || "TRADE_FINISHED".equals(trade_status)) {
|
||||
|
||||
String orderId = (String) payMessage.getPayMessage().get("out_trade_no");
|
||||
log.info("支付宝回调消息处理,发送处理消息orderId={}",orderId);
|
||||
//消息队列处理
|
||||
payNoticeProducer.sendPayNoticeMessage(orderId,"alipay");
|
||||
|
||||
payNoticeProducer.sendPayNoticeMessage(orderId, PayTypeEnum.ALI.getValue());
|
||||
log.info("支付宝回调消息发送完毕");
|
||||
return payService.getPayOutMessage("success", "成功");
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package co.yixiang.yshop.module.pay.config.handlers;
|
||||
|
||||
import co.yixiang.yshop.module.pay.enums.PayTypeEnum;
|
||||
import co.yixiang.yshop.module.pay.mq.producer.PayNoticeProducer;
|
||||
import com.egzosn.pay.common.api.PayMessageHandler;
|
||||
import com.egzosn.pay.common.api.PayService;
|
||||
@ -17,6 +18,7 @@ import java.util.Map;
|
||||
* @author hupeng
|
||||
* @date 2023/7/15
|
||||
*/
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class WxPayMessageHandler implements PayMessageHandler<WxPayMessage, PayService> {
|
||||
@ -33,7 +35,8 @@ public class WxPayMessageHandler implements PayMessageHandler<WxPayMessage, PayS
|
||||
if ("SUCCESS".equals(payMessage.getPayMessage().get("result_code"))){
|
||||
String orderId = (String) payMessage.getPayMessage().get("out_trade_no");
|
||||
//消息队列处理
|
||||
payNoticeProducer.sendPayNoticeMessage(orderId,"weixin");
|
||||
log.info("微信回调消息处理,发送处理消息orderId={}",orderId);
|
||||
payNoticeProducer.sendPayNoticeMessage(orderId, PayTypeEnum.WEIXIN.getValue());
|
||||
|
||||
return payService.getPayOutMessage("SUCCESS", "OK");
|
||||
}
|
||||
|
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (C) 2018-2022
|
||||
* All rights reserved, Designed By www.yixiang.co
|
||||
|
||||
*/
|
||||
package co.yixiang.yshop.module.pay.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author hupeng
|
||||
* 支付相关枚举
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum PayTypeEnum {
|
||||
|
||||
ALI("alipay","支付宝支付"),
|
||||
WEIXIN("weixin","微信支付"),
|
||||
WEIXIN_H5("weixin_h5","微信H5支付"),
|
||||
WEIXIN_APPLET("weixin_applet","微信小程序支付"),
|
||||
WEIXIN_APP("weixin_app","微信app支付"),
|
||||
YUE("yue","余额支付"),
|
||||
INTEGRAL("integral","积分兑换");
|
||||
|
||||
|
||||
private String value;
|
||||
private String desc;
|
||||
|
||||
public static PayTypeEnum toType(String value) {
|
||||
return Stream.of(PayTypeEnum.values())
|
||||
.filter(p -> p.value.equals(value))
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -65,7 +65,12 @@
|
||||
<groupId>co.yixiang.boot</groupId>
|
||||
<artifactId>yshop-spring-boot-starter-excel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.11.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
@ -0,0 +1,11 @@
|
||||
package co.yixiang.yshop.module.pay;
|
||||
|
||||
import co.yixiang.yshop.module.pay.strategy.PayEnum;
|
||||
|
||||
public class PayClient {
|
||||
public static void main(String[] args) {
|
||||
PayContext payContext = new PayContext();
|
||||
// System.out.println(payContext.selectPayWay(PayEnum.WEIXIN.getCode(), ));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package co.yixiang.yshop.module.pay;
|
||||
|
||||
import co.yixiang.yshop.module.pay.controller.admin.merchantdetails.vo.PayParam;
|
||||
import co.yixiang.yshop.module.pay.strategy.Pay;
|
||||
import co.yixiang.yshop.module.pay.strategy.PayFactory;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Getter
|
||||
public class PayContext {
|
||||
|
||||
private Pay pay;
|
||||
|
||||
public Map<String, String> selectPayWay(String payCode, PayParam payParam) throws Exception {
|
||||
pay = PayFactory.getInstance().create(payCode);
|
||||
return pay.selectPayWay(payCode, payParam);
|
||||
}
|
||||
|
||||
public void setPay(Pay pay) {
|
||||
this.pay = pay;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,37 @@
|
||||
package co.yixiang.yshop.module.pay.controller.admin.merchantdetails.vo;
|
||||
|
||||
import co.yixiang.yshop.module.pay.dal.dataobject.merchantdetails.MerchantDetailsDO;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class PayParam {
|
||||
/***
|
||||
* 商户订单号
|
||||
*/
|
||||
String orderId;
|
||||
|
||||
/***
|
||||
* 订单金额
|
||||
*/
|
||||
BigDecimal money;
|
||||
|
||||
/***
|
||||
* 用户openid
|
||||
*/
|
||||
String openid;
|
||||
|
||||
String ip;
|
||||
|
||||
/***
|
||||
* 支付类型 app h5 applet
|
||||
*/
|
||||
String type;
|
||||
|
||||
String payDesc;
|
||||
|
||||
MerchantDetailsDO merchantDetailsDO;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package co.yixiang.yshop.module.pay.strategy;
|
||||
|
||||
import co.yixiang.yshop.module.pay.controller.admin.merchantdetails.vo.PayParam;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface Pay {
|
||||
Map<String,String> selectPayWay(String payCode, PayParam param) throws Exception;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package co.yixiang.yshop.module.pay.strategy;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum PayEnum {
|
||||
|
||||
ALI("alipay","支付宝支付"),
|
||||
WEIXIN("weixin","微信支付"),
|
||||
WEIXIN_H5("weixin_h5","微信H5支付"),
|
||||
WEIXIN_APPLET("weixin_applet","微信小程序支付"),
|
||||
WEIXIN_APP("weixin_app","微信app支付"),
|
||||
YUE("yue","余额支付"),
|
||||
INTEGRAL("integral","积分兑换");
|
||||
|
||||
private String msg;
|
||||
private String code;
|
||||
|
||||
PayEnum( String code,String msg) {
|
||||
this.msg = msg;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package co.yixiang.yshop.module.pay.strategy;
|
||||
|
||||
import co.yixiang.yshop.module.pay.strategy.type.AliPay;
|
||||
import co.yixiang.yshop.module.pay.strategy.type.WeChatPay;
|
||||
import co.yixiang.yshop.module.pay.strategy.type.YuePay;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PayFactory {
|
||||
|
||||
private static final PayFactory factory = new PayFactory();
|
||||
|
||||
private PayFactory(){}
|
||||
|
||||
private static final Map<Object, Object> PayMap = new HashMap<>(16);
|
||||
|
||||
static {
|
||||
PayMap.put(PayEnum.ALI.getCode(),new AliPay());
|
||||
PayMap.put(PayEnum.WEIXIN.getCode(),new WeChatPay());
|
||||
PayMap.put(PayEnum.YUE.getCode(),new YuePay());
|
||||
}
|
||||
|
||||
public Pay create(String payCode){
|
||||
return (Pay) PayMap.get(payCode);
|
||||
}
|
||||
|
||||
public static PayFactory getInstance(){
|
||||
return factory;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package co.yixiang.yshop.module.pay.strategy.type;
|
||||
|
||||
import co.yixiang.yshop.module.pay.controller.admin.merchantdetails.vo.PayParam;
|
||||
import co.yixiang.yshop.module.pay.strategy.Pay;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
public class AliPay implements Pay {
|
||||
|
||||
@Override
|
||||
public Map<String,String> selectPayWay(String payCode, PayParam param) throws Exception{
|
||||
//do something
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package co.yixiang.yshop.module.pay.strategy.type;
|
||||
|
||||
import co.yixiang.yshop.module.pay.controller.admin.merchantdetails.vo.PayParam;
|
||||
import co.yixiang.yshop.module.pay.enums.PayTypeEnum;
|
||||
import co.yixiang.yshop.module.pay.strategy.Pay;
|
||||
import co.yixiang.yshop.module.pay.strategy.weixin.service.WxPayServiceUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class WeChatPay implements Pay {
|
||||
|
||||
@Override
|
||||
public Map<String, String> selectPayWay(String payCode, PayParam param) throws Exception {
|
||||
//根据微信type选择不同的支付 H5/APPLET/APP
|
||||
if (Objects.equals(param.getType(), PayTypeEnum.WEIXIN_H5.getValue())) {
|
||||
return new WxPayServiceUtils().gotoPay(param);
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package co.yixiang.yshop.module.pay.strategy.type;
|
||||
|
||||
import co.yixiang.yshop.module.pay.controller.admin.merchantdetails.vo.PayParam;
|
||||
import co.yixiang.yshop.module.pay.strategy.Pay;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
public class YuePay implements Pay {
|
||||
|
||||
@Override
|
||||
public Map<String,String> selectPayWay(String payCode,PayParam param) throws Exception{
|
||||
//do something
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package co.yixiang.yshop.module.pay.strategy.weixin.service;
|
||||
|
||||
import co.yixiang.yshop.module.pay.dal.dataobject.merchantdetails.MerchantDetailsDO;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
public interface WxPayService {
|
||||
|
||||
/**
|
||||
* 微信支付
|
||||
* @param orderTradeNo 订单编号
|
||||
* @param money 订单金额
|
||||
* @param openid 客户openid
|
||||
* @param ip 服务ip地址
|
||||
* @param type 小程序或app
|
||||
*/
|
||||
Map<String,String> gotoPay(String orderTradeNo, BigDecimal money, String openid, String ip, String type, MerchantDetailsDO merchantDetailsDO) throws Exception;
|
||||
|
||||
/**
|
||||
* 微信退款
|
||||
* @param transactionId 支付单号
|
||||
* @param outRefundNo 支付生成的退款单号
|
||||
* @param total 订单总金额
|
||||
* @param refund 退款金额
|
||||
* @throws Exception
|
||||
*/
|
||||
public Map<String,String> refund(String transactionId,String outRefundNo,BigDecimal total,BigDecimal refund,MerchantDetailsDO merchantDetailsDO) throws Exception;
|
||||
|
||||
/**
|
||||
* 保证金退款
|
||||
* @param transactionId 支付单号
|
||||
* @param outRefundNo 支付生成的退款单号
|
||||
* @param total 订单总金额
|
||||
* @param refund 退款金额
|
||||
*/
|
||||
public Map<String,String> refundBond(String transactionId,String outRefundNo,BigDecimal total,BigDecimal refund) throws Exception;
|
||||
|
||||
|
||||
/**
|
||||
* 生成收款码返回
|
||||
* @param orderFormid 订单编号
|
||||
* @param money 金额
|
||||
* @param ip IP地址
|
||||
* @param tradeType 支付类型
|
||||
* @throws Exception
|
||||
*/
|
||||
public String getCollectionCode(String orderFormid, BigDecimal money, String ip, String tradeType) throws Exception;
|
||||
|
||||
/**
|
||||
* 验签
|
||||
*/
|
||||
public boolean verifyNotify(Map<String, String> params);
|
||||
|
||||
}
|
@ -0,0 +1,261 @@
|
||||
package co.yixiang.yshop.module.pay.strategy.weixin.service;
|
||||
|
||||
import co.yixiang.yshop.module.pay.controller.admin.merchantdetails.vo.PayParam;
|
||||
import co.yixiang.yshop.module.pay.dal.dataobject.merchantdetails.MerchantDetailsDO;
|
||||
import co.yixiang.yshop.module.pay.enums.PayTypeEnum;
|
||||
import co.yixiang.yshop.module.pay.strategy.weixin.skd.PaymentApi;
|
||||
import co.yixiang.yshop.module.pay.strategy.weixin.skd.PaymentKit;
|
||||
import co.yixiang.yshop.module.pay.strategy.weixin.skd.RandomStringUtil;
|
||||
import co.yixiang.yshop.module.pay.strategy.weixin.skd.WXPayUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j(topic = "WxPayServiceUtils")
|
||||
public class WxPayServiceUtils {
|
||||
/**
|
||||
* 支付小程序appid
|
||||
*/
|
||||
@Value("${weixin.appid}")
|
||||
private String appid;
|
||||
|
||||
/**
|
||||
* 支付APP端的appid
|
||||
*/
|
||||
@Value("${weixin.app_appid}")
|
||||
private String app_appid;
|
||||
|
||||
/**
|
||||
* 支付小程序秘钥
|
||||
*/
|
||||
@Value("${weixin.secret}")
|
||||
private String secret;
|
||||
|
||||
/**
|
||||
* 商户号
|
||||
*/
|
||||
@Value("${weixin.mchid}")
|
||||
private String mch_id;
|
||||
|
||||
/**
|
||||
* 证书路径
|
||||
*/
|
||||
@Value("${weixin.certurl}")
|
||||
private String cert_url;
|
||||
|
||||
/**
|
||||
* app回调地址
|
||||
*/
|
||||
@Value("${weixin.app_notifyurl}")
|
||||
private String app_notify_url;
|
||||
|
||||
/**
|
||||
* 商户秘钥
|
||||
*/
|
||||
@Value("${weixin.key}")
|
||||
private String key;
|
||||
|
||||
|
||||
public Map<String, String> gotoPay(PayParam param) throws Exception {
|
||||
Map<String, String> reqParams = new HashMap<>();
|
||||
//获取商户信息
|
||||
//h5
|
||||
String outTradeNo = "";
|
||||
if(PayTypeEnum.WEIXIN_H5.getValue().equals(param.getType())){
|
||||
outTradeNo=param.getOrderId()+"-"+ RandomStringUtil.getRandomCode(3,0)+"H5";
|
||||
reqParams.put("appid", param.getMerchantDetailsDO().getAppid());
|
||||
//交易类型
|
||||
reqParams.put("trade_type", "MWEB");
|
||||
}else if(PayTypeEnum.WEIXIN_APP.getValue().equals(param.getType())){
|
||||
//微信app分配的APPID
|
||||
//app_appid
|
||||
outTradeNo=param.getOrderId()+"-"+ RandomStringUtil.getRandomCode(3,0)+"APP";
|
||||
reqParams.put("appid", app_appid);
|
||||
//交易类型
|
||||
reqParams.put("trade_type", "APP");
|
||||
}else if(PayTypeEnum.WEIXIN_APPLET.getValue().equals(param.getType())){
|
||||
//微信app分配的APPID
|
||||
//app_appid
|
||||
outTradeNo=param.getOrderId()+"-"+ RandomStringUtil.getRandomCode(3,0)+"APPLET";
|
||||
reqParams.put("appid", appid);
|
||||
//交易类型
|
||||
reqParams.put("trade_type", "JSAPI");
|
||||
}
|
||||
//微信支付分配的商户号
|
||||
//保存对应的outTradeNo
|
||||
|
||||
reqParams.put("mch_id", param.getMerchantDetailsDO().getMchId());
|
||||
//随机字符串
|
||||
reqParams.put("nonce_str", System.currentTimeMillis() / 1000 + "");
|
||||
//签名类型
|
||||
reqParams.put("sign_type", "MD5");
|
||||
//充值订单 商品描述
|
||||
reqParams.put("body", "充值"+param.getPayDesc()+"订单-微信支付");
|
||||
//商户订单编号
|
||||
reqParams.put("out_trade_no", outTradeNo);
|
||||
//订单总金额,单位为分
|
||||
reqParams.put("total_fee", param.getMoney().multiply(BigDecimal.valueOf(100)).intValue() + "");
|
||||
//终端IP
|
||||
reqParams.put("spbill_create_ip", param.getIp());
|
||||
//通知地址
|
||||
reqParams.put("notify_url", param.getMerchantDetailsDO().getNotifyUrl());
|
||||
//用户标识
|
||||
// reqParams.put("openid", openid);
|
||||
//签名
|
||||
String sign = WXPayUtil.generateSignature(reqParams, param.getMerchantDetailsDO().getKeyPrivate());
|
||||
reqParams.put("sign", sign);
|
||||
/*
|
||||
调用支付定义下单API,返回预付单信息 prepay_id
|
||||
*/
|
||||
String xmlResult = PaymentApi.pushOrder(reqParams);
|
||||
log.info("微信下单成功:{}",xmlResult);
|
||||
Map<String, String> result = PaymentKit.xmlToMap(xmlResult);
|
||||
//预付单信息
|
||||
String prepay_id = result.get("prepay_id");
|
||||
|
||||
/*
|
||||
小程序调起支付数据签名
|
||||
*/
|
||||
Map<String, String> packageParams = new HashMap<String, String>();
|
||||
packageParams.put("appId", param.getMerchantDetailsDO().getAppid());
|
||||
packageParams.put("timeStamp", System.currentTimeMillis() / 1000 + "");
|
||||
packageParams.put("nonceStr", System.currentTimeMillis() + "");
|
||||
packageParams.put("package", "prepay_id=" + prepay_id);
|
||||
packageParams.put("signType", param.getMerchantDetailsDO().getSignType());
|
||||
String packageSign = WXPayUtil.generateSignature(packageParams, param.getMerchantDetailsDO().getKeyPrivate());
|
||||
packageParams.put("paySign", packageSign);
|
||||
packageParams.put("codeUrl", result.get("code_url"));
|
||||
//将组合后的商户订单号返回
|
||||
packageParams.put("out_trade_no", outTradeNo);
|
||||
packageParams.put("merchant_id", param.getMerchantDetailsDO().getMchId());
|
||||
Map map = PaymentApi.queryByOutTradeNo(param.getMerchantDetailsDO().getAppid(),param.getMerchantDetailsDO().getMchId(),param.getMerchantDetailsDO().getKeyPrivate(),outTradeNo);
|
||||
log.info("查询到微信订单信息:" + map.toString());
|
||||
// payInfo.setTransaction_id(map.get("transaction_id"))
|
||||
if (PayTypeEnum.WEIXIN_H5.getValue().equals(param.getType())) {
|
||||
packageParams.put("mwebUrl", result.get("mweb_url") + "&redirect_url="+ URLEncoder.encode(param.getMerchantDetailsDO().getReturnUrl(), "GBK"));
|
||||
}
|
||||
return packageParams;
|
||||
}
|
||||
|
||||
public Map<String, String> refund(String transactionId, String outRefundNo, BigDecimal total, BigDecimal refund,MerchantDetailsDO merchantDetailsDO) throws Exception {
|
||||
//退款资金来源-可用余额退款
|
||||
String refundAccount="REFUND_SOURCE_RECHARGE_FUNDS";
|
||||
Map<String, String> params = new HashMap<>();
|
||||
if(outRefundNo.contains("APPLET")){
|
||||
//小程序微信退款
|
||||
params.put("appid", appid);
|
||||
}else if(outRefundNo.contains("APP")){
|
||||
//APP微信退款
|
||||
params.put("appid", app_appid);
|
||||
}else if(outRefundNo.contains("H5")){
|
||||
//H5
|
||||
params.put("appid", app_appid);
|
||||
}
|
||||
params.put("mch_id",mch_id);
|
||||
params.put("nonce_str", System.currentTimeMillis() / 1000 + "");
|
||||
//商户订单号和微信订单号二选一
|
||||
// params.put("out_trade_no", wxPayLog.getOutTradeNo());
|
||||
params.put("transaction_id", transactionId);
|
||||
params.put("out_refund_no", outRefundNo);
|
||||
params.put("total_fee", total.multiply(BigDecimal.valueOf(100)).intValue() + "");
|
||||
params.put("refund_fee", refund.multiply(BigDecimal.valueOf(100)).intValue() + "");
|
||||
params.put("refund_account", refundAccount);
|
||||
// 退款原因,若商户传入,会在下发给用户的退款消息中体现退款原因
|
||||
params.put("refund_desc","退款");
|
||||
//退款回调 先不做
|
||||
// params.put("notify_url", pc_refund_notify_url);
|
||||
//签名算法
|
||||
String sign = WXPayUtil.generateSignature(params,key);
|
||||
params.put("sign", sign);
|
||||
String xml = PaymentKit.toXml(params);
|
||||
log.info(xml);
|
||||
String xmlStr = WXPayUtil.doRefund("https://api.mch.weixin.qq.com/secapi/pay/refund", xml,cert_url,mch_id);
|
||||
log.info(xmlStr);
|
||||
//加入微信支付日志
|
||||
return PaymentKit.xmlToMap(xmlStr);
|
||||
}
|
||||
|
||||
public Map<String, String> refundBond(String transactionId, String outRefundNo, BigDecimal total, BigDecimal refund) throws Exception {
|
||||
//退款资金来源-可用余额退款
|
||||
String refundAccount="REFUND_SOURCE_RECHARGE_FUNDS";
|
||||
Map<String, String> params = new HashMap<>();
|
||||
if(outRefundNo.contains("XCX")){
|
||||
//小程序微信退款
|
||||
params.put("appid", appid);
|
||||
}else if(outRefundNo.contains("APP")){
|
||||
//APP微信退款
|
||||
params.put("appid", app_appid);
|
||||
}
|
||||
params.put("mch_id",mch_id);
|
||||
params.put("nonce_str", System.currentTimeMillis() / 1000 + "");
|
||||
//商户订单号和微信订单号二选一
|
||||
// params.put("out_trade_no", wxPayLog.getOutTradeNo());
|
||||
params.put("transaction_id", transactionId);
|
||||
params.put("out_refund_no", outRefundNo);
|
||||
params.put("total_fee", total.multiply(BigDecimal.valueOf(100)).intValue() + "");
|
||||
params.put("refund_fee", refund.multiply(BigDecimal.valueOf(100)).intValue() + "");
|
||||
params.put("refund_account", refundAccount);
|
||||
// 退款原因,若商户传入,会在下发给用户的退款消息中体现退款原因
|
||||
params.put("refund_desc","退款");
|
||||
//退款回调
|
||||
// params.put("notify_url", pc_bond_refund_notify_url);
|
||||
//签名算法
|
||||
String sign = WXPayUtil.generateSignature(params,key);
|
||||
params.put("sign", sign);
|
||||
String xml = PaymentKit.toXml(params);
|
||||
log.info(xml);
|
||||
String xmlStr = WXPayUtil.doRefund("https://api.mch.weixin.qq.com/secapi/pay/refund", xml,cert_url,mch_id);
|
||||
log.info(xmlStr);
|
||||
//加入微信支付日志
|
||||
Map map = PaymentKit.xmlToMap(xmlStr);
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
public String getCollectionCode(String orderTradeNo, BigDecimal money, String ip, String tradeType) throws Exception {
|
||||
Map<String, String> reqParams = new HashMap<>();
|
||||
//微信分配的小程序ID
|
||||
reqParams.put("appid",appid);
|
||||
//微信支付分配的商户号
|
||||
reqParams.put("mch_id",mch_id);
|
||||
//随机字符串
|
||||
reqParams.put("nonce_str", System.currentTimeMillis() / 1000 + "");
|
||||
//签名类型
|
||||
reqParams.put("sign_type", "MD5");
|
||||
//充值订单 商品描述
|
||||
reqParams.put("body", "充值"+orderTradeNo+"订单-微信小程序");
|
||||
|
||||
//商户订单编号
|
||||
reqParams.put("out_trade_no", orderTradeNo);
|
||||
//订单总金额,单位为分
|
||||
reqParams.put("total_fee", money.multiply(BigDecimal.valueOf(100)).intValue() + "");
|
||||
//终端IP
|
||||
// reqParams.put("spbill_create_ip", "127.0.0.1");
|
||||
reqParams.put("spbill_create_ip", ip);
|
||||
//支付回调地址
|
||||
// reqParams.put("notify_url", pc_notify_url);
|
||||
//交易类型
|
||||
reqParams.put("trade_type", tradeType);
|
||||
//用户标识
|
||||
// reqParams.put("openid", openid);
|
||||
//签名
|
||||
String sign = WXPayUtil.generateSignature(reqParams,key);
|
||||
reqParams.put("sign", sign);
|
||||
/*
|
||||
调用支付定义下单API,返回预付单信息 prepay_id
|
||||
*/
|
||||
String xmlResult = PaymentApi.pushOrder(reqParams);
|
||||
log.info(xmlResult);
|
||||
Map<String, String> result = PaymentKit.xmlToMap(xmlResult);
|
||||
return result.get("code_url");
|
||||
}
|
||||
|
||||
public boolean verifyNotify(Map<String, String> params){
|
||||
return PaymentKit.verifyNotify(params,key);
|
||||
}
|
||||
}
|
@ -0,0 +1,273 @@
|
||||
package co.yixiang.yshop.module.pay.strategy.weixin.service.impl;
|
||||
|
||||
import co.yixiang.yshop.module.pay.dal.dataobject.merchantdetails.MerchantDetailsDO;
|
||||
import co.yixiang.yshop.module.pay.enums.PayTypeEnum;
|
||||
import co.yixiang.yshop.module.pay.strategy.weixin.service.WxPayService;
|
||||
import co.yixiang.yshop.module.pay.strategy.weixin.skd.PaymentApi;
|
||||
import co.yixiang.yshop.module.pay.strategy.weixin.skd.PaymentKit;
|
||||
import co.yixiang.yshop.module.pay.strategy.weixin.skd.RandomStringUtil;
|
||||
import co.yixiang.yshop.module.pay.strategy.weixin.skd.WXPayUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@Slf4j(topic = "WxPayServiceImpl")
|
||||
public class WxPayServiceImpl implements WxPayService {
|
||||
/**
|
||||
* 支付小程序appid
|
||||
*/
|
||||
@Value("${weixin.appid}")
|
||||
private String appid;
|
||||
|
||||
/**
|
||||
* 支付APP端的appid
|
||||
*/
|
||||
@Value("${weixin.app_appid}")
|
||||
private String app_appid;
|
||||
|
||||
/**
|
||||
* 支付小程序秘钥
|
||||
*/
|
||||
@Value("${weixin.secret}")
|
||||
private String secret;
|
||||
|
||||
/**
|
||||
* 商户号
|
||||
*/
|
||||
@Value("${weixin.mchid}")
|
||||
private String mch_id;
|
||||
|
||||
/**
|
||||
* 证书路径
|
||||
*/
|
||||
@Value("${weixin.certurl}")
|
||||
private String cert_url;
|
||||
|
||||
/**
|
||||
* app回调地址
|
||||
*/
|
||||
@Value("${weixin.app_notifyurl}")
|
||||
private String app_notify_url;
|
||||
|
||||
/**
|
||||
* 商户秘钥
|
||||
*/
|
||||
@Value("${weixin.key}")
|
||||
private String key;
|
||||
|
||||
|
||||
@Override
|
||||
public Map<String, String> gotoPay(String orderId, BigDecimal money, String openid, String ip, String type, MerchantDetailsDO merchantDetailsDO) throws Exception {
|
||||
Map<String, String> reqParams = new HashMap<>();
|
||||
//获取商户信息
|
||||
//h5
|
||||
String outTradeNo = "";
|
||||
if(PayTypeEnum.WEIXIN_H5.getValue().equals(type)){
|
||||
outTradeNo=orderId+"-"+ RandomStringUtil.getRandomCode(3,0)+"H5";
|
||||
reqParams.put("appid", merchantDetailsDO.getAppid());
|
||||
//交易类型
|
||||
reqParams.put("trade_type", "MWEB");
|
||||
}else if(PayTypeEnum.WEIXIN_APP.getValue().equals(type)){
|
||||
//微信app分配的APPID
|
||||
//app_appid
|
||||
outTradeNo=orderId+"-"+ RandomStringUtil.getRandomCode(3,0)+"APP";
|
||||
reqParams.put("appid", app_appid);
|
||||
//交易类型
|
||||
reqParams.put("trade_type", "APP");
|
||||
}else if(PayTypeEnum.WEIXIN_APPLET.getValue().equals(type)){
|
||||
//微信app分配的APPID
|
||||
//app_appid
|
||||
outTradeNo=orderId+"-"+ RandomStringUtil.getRandomCode(3,0)+"APPLET";
|
||||
reqParams.put("appid", appid);
|
||||
//交易类型
|
||||
reqParams.put("trade_type", "JSAPI");
|
||||
}
|
||||
//微信支付分配的商户号
|
||||
//保存对应的outTradeNo
|
||||
|
||||
reqParams.put("mch_id", merchantDetailsDO.getMchId());
|
||||
//随机字符串
|
||||
reqParams.put("nonce_str", System.currentTimeMillis() / 1000 + "");
|
||||
//签名类型
|
||||
reqParams.put("sign_type", "MD5");
|
||||
//充值订单 商品描述
|
||||
reqParams.put("body", "充值"+outTradeNo+"订单-微信支付");
|
||||
//商户订单编号
|
||||
reqParams.put("out_trade_no", outTradeNo);
|
||||
//订单总金额,单位为分
|
||||
reqParams.put("total_fee", money.multiply(BigDecimal.valueOf(100)).intValue() + "");
|
||||
//终端IP
|
||||
reqParams.put("spbill_create_ip", ip);
|
||||
//通知地址
|
||||
reqParams.put("notify_url", merchantDetailsDO.getNotifyUrl());
|
||||
//用户标识
|
||||
// reqParams.put("openid", openid);
|
||||
//签名
|
||||
String sign = WXPayUtil.generateSignature(reqParams, merchantDetailsDO.getKeyPrivate());
|
||||
reqParams.put("sign", sign);
|
||||
/*
|
||||
调用支付定义下单API,返回预付单信息 prepay_id
|
||||
*/
|
||||
String xmlResult = PaymentApi.pushOrder(reqParams);
|
||||
log.info(xmlResult);
|
||||
Map<String, String> result = PaymentKit.xmlToMap(xmlResult);
|
||||
//预付单信息
|
||||
String prepay_id = result.get("prepay_id");
|
||||
|
||||
/*
|
||||
小程序调起支付数据签名
|
||||
*/
|
||||
Map<String, String> packageParams = new HashMap<String, String>();
|
||||
packageParams.put("appId", merchantDetailsDO.getAppid());
|
||||
packageParams.put("timeStamp", System.currentTimeMillis() / 1000 + "");
|
||||
packageParams.put("nonceStr", System.currentTimeMillis() + "");
|
||||
packageParams.put("package", "prepay_id=" + prepay_id);
|
||||
packageParams.put("signType", merchantDetailsDO.getSignType());
|
||||
String packageSign = WXPayUtil.generateSignature(packageParams, merchantDetailsDO.getKeyPrivate());
|
||||
packageParams.put("paySign", packageSign);
|
||||
packageParams.put("codeUrl", result.get("code_url"));
|
||||
//将组合后的商户订单号返回
|
||||
packageParams.put("out_trade_no", outTradeNo);
|
||||
packageParams.put("orderId", orderId);
|
||||
packageParams.put("merchant_id", merchantDetailsDO.getMchId());
|
||||
Map map = PaymentApi.queryByOutTradeNo(merchantDetailsDO.getAppid(),merchantDetailsDO.getMchId(),merchantDetailsDO.getKeyPrivate(),outTradeNo);
|
||||
log.info("查询到微信订单信息:" + map.toString());
|
||||
// payInfo.setTransaction_id(map.get("transaction_id"))
|
||||
if (PayTypeEnum.WEIXIN_H5.getValue().equals(type)) {
|
||||
packageParams.put("mwebUrl", result.get("mweb_url") + "&redirect_url="+ URLEncoder.encode(merchantDetailsDO.getReturnUrl(), "GBK"));
|
||||
}
|
||||
return packageParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> refund(String transactionId, String outRefundNo, BigDecimal total, BigDecimal refund,MerchantDetailsDO merchantDetailsDO) throws Exception {
|
||||
//退款资金来源-可用余额退款
|
||||
String refundAccount="REFUND_SOURCE_RECHARGE_FUNDS";
|
||||
Map<String, String> params = new HashMap<>();
|
||||
if(outRefundNo.contains("APPLET")){
|
||||
//小程序微信退款
|
||||
params.put("appid", appid);
|
||||
}else if(outRefundNo.contains("APP")){
|
||||
//APP微信退款
|
||||
params.put("appid", app_appid);
|
||||
}else if(outRefundNo.contains("H5")){
|
||||
//H5
|
||||
params.put("appid", app_appid);
|
||||
}
|
||||
params.put("mch_id",mch_id);
|
||||
params.put("nonce_str", System.currentTimeMillis() / 1000 + "");
|
||||
//商户订单号和微信订单号二选一
|
||||
// params.put("out_trade_no", wxPayLog.getOutTradeNo());
|
||||
params.put("transaction_id", transactionId);
|
||||
params.put("out_refund_no", outRefundNo);
|
||||
params.put("total_fee", total.multiply(BigDecimal.valueOf(100)).intValue() + "");
|
||||
params.put("refund_fee", refund.multiply(BigDecimal.valueOf(100)).intValue() + "");
|
||||
params.put("refund_account", refundAccount);
|
||||
// 退款原因,若商户传入,会在下发给用户的退款消息中体现退款原因
|
||||
params.put("refund_desc","退款");
|
||||
//退款回调 先不做
|
||||
// params.put("notify_url", pc_refund_notify_url);
|
||||
//签名算法
|
||||
String sign = WXPayUtil.generateSignature(params,key);
|
||||
params.put("sign", sign);
|
||||
String xml = PaymentKit.toXml(params);
|
||||
log.info(xml);
|
||||
String cert_url = "/yshop-server/apiclient_cert.p12";
|
||||
log.info("certUrl:{}",cert_url);
|
||||
String xmlStr = WXPayUtil.doRefund("https://api.mch.weixin.qq.com/secapi/pay/refund", xml,cert_url,mch_id);
|
||||
log.info(xmlStr);
|
||||
//加入微信支付日志
|
||||
return PaymentKit.xmlToMap(xmlStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> refundBond(String transactionId, String outRefundNo, BigDecimal total, BigDecimal refund) throws Exception {
|
||||
//退款资金来源-可用余额退款
|
||||
String refundAccount="REFUND_SOURCE_RECHARGE_FUNDS";
|
||||
Map<String, String> params = new HashMap<>();
|
||||
if(outRefundNo.contains("XCX")){
|
||||
//小程序微信退款
|
||||
params.put("appid", appid);
|
||||
}else if(outRefundNo.contains("APP")){
|
||||
//APP微信退款
|
||||
params.put("appid", app_appid);
|
||||
}
|
||||
params.put("mch_id",mch_id);
|
||||
params.put("nonce_str", System.currentTimeMillis() / 1000 + "");
|
||||
//商户订单号和微信订单号二选一
|
||||
// params.put("out_trade_no", wxPayLog.getOutTradeNo());
|
||||
params.put("transaction_id", transactionId);
|
||||
params.put("out_refund_no", outRefundNo);
|
||||
params.put("total_fee", total.multiply(BigDecimal.valueOf(100)).intValue() + "");
|
||||
params.put("refund_fee", refund.multiply(BigDecimal.valueOf(100)).intValue() + "");
|
||||
params.put("refund_account", refundAccount);
|
||||
// 退款原因,若商户传入,会在下发给用户的退款消息中体现退款原因
|
||||
params.put("refund_desc","退款");
|
||||
//退款回调
|
||||
// params.put("notify_url", pc_bond_refund_notify_url);
|
||||
//签名算法
|
||||
String sign = WXPayUtil.generateSignature(params,key);
|
||||
params.put("sign", sign);
|
||||
String xml = PaymentKit.toXml(params);
|
||||
log.info(xml);
|
||||
String xmlStr = WXPayUtil.doRefund("https://api.mch.weixin.qq.com/secapi/pay/refund", xml,cert_url,mch_id);
|
||||
log.info(xmlStr);
|
||||
//加入微信支付日志
|
||||
Map map = PaymentKit.xmlToMap(xmlStr);
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCollectionCode(String orderTradeNo, BigDecimal money, String ip, String tradeType) throws Exception {
|
||||
Map<String, String> reqParams = new HashMap<>();
|
||||
//微信分配的小程序ID
|
||||
reqParams.put("appid",appid);
|
||||
//微信支付分配的商户号
|
||||
reqParams.put("mch_id",mch_id);
|
||||
//随机字符串
|
||||
reqParams.put("nonce_str", System.currentTimeMillis() / 1000 + "");
|
||||
//签名类型
|
||||
reqParams.put("sign_type", "MD5");
|
||||
//充值订单 商品描述
|
||||
reqParams.put("body", "充值"+orderTradeNo+"订单-微信小程序");
|
||||
|
||||
//商户订单编号
|
||||
reqParams.put("out_trade_no", orderTradeNo);
|
||||
//订单总金额,单位为分
|
||||
reqParams.put("total_fee", money.multiply(BigDecimal.valueOf(100)).intValue() + "");
|
||||
//终端IP
|
||||
// reqParams.put("spbill_create_ip", "127.0.0.1");
|
||||
reqParams.put("spbill_create_ip", ip);
|
||||
//支付回调地址
|
||||
// reqParams.put("notify_url", pc_notify_url);
|
||||
//交易类型
|
||||
reqParams.put("trade_type", tradeType);
|
||||
//用户标识
|
||||
// reqParams.put("openid", openid);
|
||||
//签名
|
||||
String sign = WXPayUtil.generateSignature(reqParams,key);
|
||||
reqParams.put("sign", sign);
|
||||
/*
|
||||
调用支付定义下单API,返回预付单信息 prepay_id
|
||||
*/
|
||||
String xmlResult = PaymentApi.pushOrder(reqParams);
|
||||
log.info(xmlResult);
|
||||
Map<String, String> result = PaymentKit.xmlToMap(xmlResult);
|
||||
return result.get("code_url");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifyNotify(Map<String, String> params){
|
||||
return PaymentKit.verifyNotify(params,key);
|
||||
}
|
||||
}
|
@ -0,0 +1,260 @@
|
||||
package co.yixiang.yshop.module.pay.strategy.weixin.skd;
|
||||
|
||||
import javax.net.ssl.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
public class HttpKit {
|
||||
private static final String GET = "GET";
|
||||
private static final String POST = "POST";
|
||||
private static String CHARSET = "UTF-8";
|
||||
private static final SSLSocketFactory sslSocketFactory = initSSLSocketFactory();
|
||||
private static final TrustAnyHostnameVerifier trustAnyHostnameVerifier = new HttpKit().new TrustAnyHostnameVerifier();
|
||||
|
||||
// public static final OkHttp3Delegate delegate = new OkHttp3Delegate();
|
||||
|
||||
private HttpKit() {
|
||||
}
|
||||
|
||||
private static SSLSocketFactory initSSLSocketFactory() {
|
||||
try {
|
||||
TrustManager[] e = new TrustManager[]{new HttpKit().new TrustAnyTrustManager()};
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init((KeyManager[])null, e, new SecureRandom());
|
||||
return sslContext.getSocketFactory();
|
||||
} catch (Exception var2) {
|
||||
throw new RuntimeException(var2);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setCharSet(String charSet) {
|
||||
if(charSet!=null && !charSet.equals("")) {
|
||||
throw new IllegalArgumentException("charSet can not be blank.");
|
||||
} else {
|
||||
CHARSET = charSet;
|
||||
}
|
||||
}
|
||||
|
||||
private static HttpURLConnection getHttpConnection(String url, String method, Map<String, String> headers) throws IOException, NoSuchAlgorithmException, NoSuchProviderException, KeyManagementException {
|
||||
URL _url = new URL(url);
|
||||
HttpURLConnection conn = (HttpURLConnection)_url.openConnection();
|
||||
if(conn instanceof HttpsURLConnection) {
|
||||
((HttpsURLConnection)conn).setSSLSocketFactory(sslSocketFactory);
|
||||
((HttpsURLConnection)conn).setHostnameVerifier(trustAnyHostnameVerifier);
|
||||
}
|
||||
|
||||
conn.setRequestMethod(method);
|
||||
conn.setDoOutput(true);
|
||||
conn.setDoInput(true);
|
||||
conn.setConnectTimeout(19000);
|
||||
conn.setReadTimeout(19000);
|
||||
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
||||
conn.setRequestProperty("AuthUser-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36");
|
||||
if(headers != null && !headers.isEmpty()) {
|
||||
Iterator i$ = headers.entrySet().iterator();
|
||||
|
||||
while(i$.hasNext()) {
|
||||
Map.Entry entry = (Map.Entry)i$.next();
|
||||
conn.setRequestProperty((String)entry.getKey(), (String)entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
public static String get(String url, Map<String, String> queryParas, Map<String, String> headers) {
|
||||
HttpURLConnection conn = null;
|
||||
String e;
|
||||
try {
|
||||
conn = getHttpConnection(buildUrlWithQueryString(url, queryParas), "GET", headers);
|
||||
conn.connect();
|
||||
e = readResponseString(conn);
|
||||
} catch (Exception var8) {
|
||||
throw new RuntimeException(var8);
|
||||
} finally {
|
||||
if(conn != null) {
|
||||
conn.disconnect();
|
||||
}
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
public static String get(String url, Map<String, String> queryParas) {
|
||||
return get(url, queryParas, (Map)null);
|
||||
}
|
||||
|
||||
public static String get(String url) {
|
||||
return get(url, (Map)null, (Map)null);
|
||||
}
|
||||
|
||||
public static String post(String url, Map<String, String> queryParas, String data, Map<String, String> headers) {
|
||||
HttpURLConnection conn = null;
|
||||
|
||||
String var6;
|
||||
try {
|
||||
conn = getHttpConnection(buildUrlWithQueryString(url, queryParas), "POST", headers);
|
||||
conn.connect();
|
||||
OutputStream e = conn.getOutputStream();
|
||||
e.write(data.getBytes(CHARSET));
|
||||
e.flush();
|
||||
e.close();
|
||||
var6 = readResponseString(conn);
|
||||
} catch (Exception var10) {
|
||||
throw new RuntimeException(var10);
|
||||
} finally {
|
||||
if(conn != null) {
|
||||
conn.disconnect();
|
||||
}
|
||||
}
|
||||
return var6;
|
||||
}
|
||||
|
||||
public static String post(String url, Map<String, String> queryParas, String data) {
|
||||
return post(url, queryParas, data, (Map)null);
|
||||
}
|
||||
|
||||
public static String post(String url, String data, Map<String, String> headers) {
|
||||
return post(url, (Map)null, data, headers);
|
||||
}
|
||||
|
||||
public static String post(String url, String data) {
|
||||
return post(url, (Map)null, data, (Map)null);
|
||||
}
|
||||
|
||||
private static String readResponseString(HttpURLConnection conn) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
InputStream inputStream = null;
|
||||
|
||||
try {
|
||||
inputStream = conn.getInputStream();
|
||||
BufferedReader e = new BufferedReader(new InputStreamReader(inputStream, CHARSET));
|
||||
String line = null;
|
||||
|
||||
while((line = e.readLine()) != null) {
|
||||
sb.append(line).append("\n");
|
||||
}
|
||||
|
||||
String var5 = sb.toString();
|
||||
return var5;
|
||||
} catch (Exception var14) {
|
||||
throw new RuntimeException(var14);
|
||||
} finally {
|
||||
if(inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException var13) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static String buildUrlWithQueryString(String url, Map<String, String> queryParas) {
|
||||
if(queryParas != null && !queryParas.isEmpty()) {
|
||||
StringBuilder sb = new StringBuilder(url);
|
||||
boolean isFirst;
|
||||
if(url.indexOf("?") == -1) {
|
||||
isFirst = true;
|
||||
sb.append("?");
|
||||
} else {
|
||||
isFirst = false;
|
||||
}
|
||||
|
||||
String key;
|
||||
String value;
|
||||
for(Iterator i$ = queryParas.entrySet().iterator(); i$.hasNext(); sb.append(key).append("=").append(value)) {
|
||||
Map.Entry entry = (Map.Entry)i$.next();
|
||||
if(isFirst) {
|
||||
isFirst = false;
|
||||
} else {
|
||||
sb.append("&");
|
||||
}
|
||||
|
||||
key = (String)entry.getKey();
|
||||
value = (String)entry.getValue();
|
||||
if(value!=null && !value.equals("")) {
|
||||
try {
|
||||
value = URLEncoder.encode(value, CHARSET);
|
||||
} catch (UnsupportedEncodingException var9) {
|
||||
throw new RuntimeException(var9);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
} else {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
public static String readData(HttpServletRequest request) {
|
||||
BufferedReader br = null;
|
||||
|
||||
try {
|
||||
StringBuilder e = new StringBuilder();
|
||||
br = request.getReader();
|
||||
String line = null;
|
||||
|
||||
while((line = br.readLine()) != null) {
|
||||
e.append(line).append("\n");
|
||||
}
|
||||
|
||||
line = e.toString();
|
||||
return line;
|
||||
} catch (IOException var12) {
|
||||
throw new RuntimeException(var12);
|
||||
} finally {
|
||||
if(br != null) {
|
||||
try {
|
||||
br.close();
|
||||
} catch (IOException var11) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
@Deprecated
|
||||
public static String readIncommingRequestData(HttpServletRequest request) {
|
||||
return readData(request);
|
||||
}
|
||||
|
||||
|
||||
private class TrustAnyTrustManager implements X509TrustManager {
|
||||
private TrustAnyTrustManager() {
|
||||
}
|
||||
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||
}
|
||||
}
|
||||
|
||||
private class TrustAnyHostnameVerifier implements HostnameVerifier {
|
||||
private TrustAnyHostnameVerifier() {
|
||||
}
|
||||
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package co.yixiang.yshop.module.pay.strategy.weixin.skd;
|
||||
|
||||
import org.apache.commons.io.Charsets;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* IOUtils
|
||||
* @author majker
|
||||
*/
|
||||
public abstract class IOUtils {
|
||||
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
|
||||
|
||||
/**
|
||||
* closeQuietly
|
||||
* @param closeable 自动关闭
|
||||
*/
|
||||
public static void closeQuietly(Closeable closeable) {
|
||||
try {
|
||||
if (closeable != null) {
|
||||
closeable.close();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* InputStream to String utf-8
|
||||
*
|
||||
* @param input the <code>InputStream</code> to read from
|
||||
* @return the requested String
|
||||
* @throws NullPointerException if the input is null
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public static String toString(InputStream input) throws IOException {
|
||||
return toString(input, Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* InputStream to String
|
||||
*
|
||||
* @param input the <code>InputStream</code> to read from
|
||||
* @param charset the <code>Charset</code>
|
||||
* @return the requested String
|
||||
* @throws NullPointerException if the input is null
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public static String toString(InputStream input, Charset charset) throws IOException {
|
||||
InputStreamReader in = new InputStreamReader(input, charset);
|
||||
StringBuffer out = new StringBuffer();
|
||||
char[] c = new char[DEFAULT_BUFFER_SIZE];
|
||||
for (int n; (n = in.read(c)) != -1;) {
|
||||
out.append(new String(c, 0, n));
|
||||
}
|
||||
IOUtils.closeQuietly(in);
|
||||
IOUtils.closeQuietly(input);
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* InputStream to File
|
||||
* @param input the <code>InputStream</code> to read from
|
||||
* @param file the File to write
|
||||
* @throws IOException id异常
|
||||
*/
|
||||
public static void toFile(InputStream input, File file) throws IOException {
|
||||
OutputStream os = new FileOutputStream(file);
|
||||
int bytesRead = 0;
|
||||
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
||||
while ((bytesRead = input.read(buffer, 0, DEFAULT_BUFFER_SIZE)) != -1) {
|
||||
os.write(buffer, 0, bytesRead);
|
||||
}
|
||||
IOUtils.closeQuietly(os);
|
||||
IOUtils.closeQuietly(input);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,277 @@
|
||||
package co.yixiang.yshop.module.pay.strategy.weixin.skd;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/****************************************************
|
||||
*
|
||||
* 微信支付api
|
||||
*
|
||||
* @author majker
|
||||
* @version 1.0
|
||||
**************************************************/
|
||||
public class PaymentApi {
|
||||
|
||||
private PaymentApi() {}
|
||||
|
||||
// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
|
||||
private static String unifiedOrderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
|
||||
|
||||
/**
|
||||
* 交易类型枚举
|
||||
* WAP的文档:https://pay.weixin.qq.com/wiki/doc/api/wap.php?chapter=15_1
|
||||
* @author L.cm
|
||||
* <pre>
|
||||
* email: 596392912@qq.com
|
||||
* site: http://www.dreamlu.net
|
||||
* date: 2015年10月27日 下午9:46:27
|
||||
* </pre>
|
||||
*/
|
||||
public enum TradeType {
|
||||
JSAPI, NATIVE, APP, WAP, MWEB
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一下单
|
||||
* @param params 参数map
|
||||
* @return String
|
||||
*/
|
||||
public static String pushOrder(Map<String, String> params) {
|
||||
return HttpKit.post(unifiedOrderUrl, PaymentKit.toXml(params));
|
||||
}
|
||||
|
||||
private static Map<String, String> request(String url, Map<String, String> params, String paternerKey) {
|
||||
params.put("nonce_str", System.currentTimeMillis() + "");
|
||||
String sign = PaymentKit.createSign(params, paternerKey);
|
||||
params.put("sign", sign);
|
||||
String xmlStr = HttpKit.post(url, PaymentKit.toXml(params));
|
||||
return PaymentKit.xmlToMap(xmlStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文档说明:https://pay.weixin.qq.com/wiki/doc/api/wap.php?chapter=15_4
|
||||
* <pre>
|
||||
* @param appId 公众账号ID 是 String(32) wx8888888888888888 微信分配的公众账号ID
|
||||
* 随机字符串 noncestr 是 String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,不长于32位。推荐随机数生成算法
|
||||
* 订单详情扩展字符串 package 是 String(32) WAP 扩展字段,固定填写WAP
|
||||
* @param prepayId 预支付交易会话标识 是 String(64) wx201410272009395522657a690389285100 微信统一下单接口返回的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时
|
||||
* 签名 sign 是 String(32) C380BEC2BFD727A4B6845133519F3AD6 签名,详见签名生成算法
|
||||
* 时间戳 timestamp 是 String(32) 1414561699 当前的时间,其他详见时间戳规则
|
||||
* @param paternerKey 签名密匙
|
||||
* </pre>
|
||||
* @return {String}
|
||||
*/
|
||||
public static String getDeepLink(String appId, String prepayId, String paternerKey) {
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("appid", appId);
|
||||
params.put("noncestr", System.currentTimeMillis() + "");
|
||||
params.put("package", "WAP");
|
||||
params.put("prepayid", prepayId);
|
||||
params.put("timestamp", System.currentTimeMillis() / 1000 + "");
|
||||
String sign = PaymentKit.createSign(params, paternerKey);
|
||||
params.put("sign", sign);
|
||||
|
||||
String string1 = PaymentKit.packageSign(params, true);
|
||||
|
||||
String string2 = "";
|
||||
try { string2 = PaymentKit.urlEncode(string1); } catch (UnsupportedEncodingException e) {}
|
||||
|
||||
return "wechat://wap/pay?" + string2;
|
||||
}
|
||||
|
||||
// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2
|
||||
private static String orderQueryUrl = "https://api.mch.weixin.qq.com/pay/orderquery";
|
||||
|
||||
/**
|
||||
* 根据商户订单号查询信息
|
||||
* @param appid 公众账号ID
|
||||
* @param mch_id 商户号
|
||||
* @param paternerKey 商户密钥
|
||||
* @param transaction_id 微信订单号
|
||||
* @return 回调信息
|
||||
*/
|
||||
public static Map<String, String> queryByTransactionId(String appid, String mch_id, String paternerKey, String transaction_id) {
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("appid", appid);
|
||||
params.put("mch_id", mch_id);
|
||||
params.put("transaction_id", transaction_id);
|
||||
return request(orderQueryUrl, params, paternerKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据商户订单号查询信息
|
||||
* @param appid 公众账号ID
|
||||
* @param mch_id 商户号
|
||||
* @param paternerKey 商户密钥
|
||||
* @param out_trade_no 商户订单号
|
||||
* @return 回调信息
|
||||
*/
|
||||
public static Map<String, String> queryByOutTradeNo(String appid, String mch_id, String paternerKey, String out_trade_no) {
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("appid", appid);
|
||||
params.put("mch_id", mch_id);
|
||||
params.put("out_trade_no", out_trade_no);
|
||||
return request(orderQueryUrl, params, paternerKey);
|
||||
}
|
||||
|
||||
// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_3
|
||||
private static String closeOrderUrl = "https://api.mch.weixin.qq.com/pay/closeorder";
|
||||
|
||||
/**
|
||||
* 关闭订单
|
||||
* @param appid 公众账号ID
|
||||
* @param mch_id 商户号
|
||||
* @param paternerKey 商户密钥
|
||||
* @param out_trade_no 商户订单号
|
||||
* @return 回调信息
|
||||
*/
|
||||
public static Map<String, String> closeOrder(String appid, String mch_id, String paternerKey, String out_trade_no) {
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("appid", appid);
|
||||
params.put("mch_id", mch_id);
|
||||
params.put("out_trade_no", out_trade_no);
|
||||
return request(closeOrderUrl, params, paternerKey);
|
||||
}
|
||||
|
||||
// 申请退款文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
|
||||
public static String refundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund";
|
||||
|
||||
|
||||
// 查询退款文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5
|
||||
private static String refundQueryUrl = "https://api.mch.weixin.qq.com/pay/refundquery";
|
||||
|
||||
private static Map<String, String> baseRefundQuery(Map<String, String> params, String appid, String mch_id, String paternerKey) {
|
||||
params.put("appid", appid);
|
||||
params.put("mch_id", mch_id);
|
||||
return request(refundQueryUrl, params, paternerKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据微信订单号查询退款
|
||||
* @param appid 公众账号ID
|
||||
* @param mch_id 商户号
|
||||
* @param paternerKey 商户密钥
|
||||
* @param transaction_id 微信订单号
|
||||
* @return map
|
||||
*/
|
||||
public static Map<String, String> refundQueryByTransactionId(String appid, String mch_id, String paternerKey, String transaction_id) {
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("transaction_id", transaction_id);
|
||||
return baseRefundQuery(params, appid, mch_id, paternerKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据商户订单号查询退款
|
||||
* @param appid 公众账号ID
|
||||
* @param mch_id 商户号
|
||||
* @param paternerKey 商户密钥
|
||||
* @param out_trade_no 商户订单号
|
||||
* @return map
|
||||
*/
|
||||
public static Map<String, String> refundQueryByOutTradeNo(String appid, String mch_id, String paternerKey, String out_trade_no) {
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("out_trade_no", out_trade_no);
|
||||
return baseRefundQuery(params, appid, mch_id, paternerKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据微信订单号查询退款
|
||||
* @param appid 公众账号ID
|
||||
* @param mch_id 商户号
|
||||
* @param paternerKey 商户密钥
|
||||
* @param out_refund_no 商户退款单号
|
||||
* @return map
|
||||
*/
|
||||
public static Map<String, String> refundQueryByOutRefundNo(String appid, String mch_id, String paternerKey, String out_refund_no) {
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("out_refund_no", out_refund_no);
|
||||
return baseRefundQuery(params, appid, mch_id, paternerKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据微信订单号查询退款
|
||||
* @param appid 公众账号ID
|
||||
* @param mch_id 商户号
|
||||
* @param paternerKey 商户密钥
|
||||
* @param refund_id 微信退款单号
|
||||
* @return map
|
||||
*/
|
||||
public static Map<String, String> refundQueryByRefundId(String appid, String mch_id, String paternerKey, String refund_id) {
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("refund_id", refund_id);
|
||||
return baseRefundQuery(params, appid, mch_id, paternerKey);
|
||||
}
|
||||
|
||||
private static String downloadBillUrl = "https://api.mch.weixin.qq.com/pay/downloadbill";
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* ALL,返回当日所有订单信息,默认值
|
||||
* SUCCESS,返回当日成功支付的订单
|
||||
* REFUND,返回当日退款订单
|
||||
* REVOKED,已撤销的订单
|
||||
* </pre>
|
||||
*/
|
||||
public static enum BillType {
|
||||
ALL, SUCCESS, REFUND, REVOKED
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载对账单
|
||||
* <pre>
|
||||
* 公众账号ID appid 是 String(32) wx8888888888888888 微信分配的公众账号ID(企业号corpid即为此appId)
|
||||
* 商户号 mch_id 是 String(32) 1900000109 微信支付分配的商户号
|
||||
* 设备号 device_info 否 String(32) 013467007045764 微信支付分配的终端设备号
|
||||
* 随机字符串 nonce_str 是 String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,不长于32位。推荐随机数生成算法
|
||||
* 签名 sign 是 String(32) C380BEC2BFD727A4B6845133519F3AD6 签名,详见签名生成算法
|
||||
* 对账单日期 bill_date 是 String(8) 20140603 下载对账单的日期,格式:20140603
|
||||
* 账单类型 bill_type 否 String(8)
|
||||
* </pre>
|
||||
* @param appid 公众账号ID
|
||||
* @param mch_id 商户号
|
||||
* @param paternerKey 签名密匙
|
||||
* @param billDate 对账单日期
|
||||
* @return String
|
||||
*/
|
||||
public static String downloadBill(String appid, String mch_id, String paternerKey, String billDate) {
|
||||
return downloadBill(appid, mch_id, paternerKey, billDate, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载对账单
|
||||
* <pre>
|
||||
* 公众账号ID appid 是 String(32) wx8888888888888888 微信分配的公众账号ID(企业号corpid即为此appId)
|
||||
* 商户号 mch_id 是 String(32) 1900000109 微信支付分配的商户号
|
||||
* 设备号 device_info 否 String(32) 013467007045764 微信支付分配的终端设备号
|
||||
* 随机字符串 nonce_str 是 String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,不长于32位。推荐随机数生成算法
|
||||
* 签名 sign 是 String(32) C380BEC2BFD727A4B6845133519F3AD6 签名,详见签名生成算法
|
||||
* 对账单日期 bill_date 是 String(8) 20140603 下载对账单的日期,格式:20140603
|
||||
* 账单类型 bill_type 否 String(8)
|
||||
* </pre>
|
||||
* @param appid 公众账号ID
|
||||
* @param mch_id 商户号
|
||||
* @param paternerKey 签名密匙
|
||||
* @param billDate 对账单日期
|
||||
* @param billType 账单类型
|
||||
* @return String
|
||||
*/
|
||||
public static String downloadBill(String appid, String mch_id, String paternerKey, String billDate, BillType billType) {
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("appid", appid);
|
||||
params.put("mch_id", mch_id);
|
||||
params.put("nonce_str", System.currentTimeMillis() + "");
|
||||
params.put("bill_date", billDate);
|
||||
if (null != billType) {
|
||||
params.put("bill_type", billType.name());
|
||||
} else {
|
||||
params.put("bill_type", BillType.ALL.name());
|
||||
}
|
||||
String sign = PaymentKit.createSign(params, paternerKey);
|
||||
params.put("sign", sign);
|
||||
return HttpKit.post(downloadBillUrl, PaymentKit.toXml(params));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,120 @@
|
||||
package co.yixiang.yshop.module.pay.strategy.weixin.skd;
|
||||
|
||||
|
||||
import org.apache.commons.io.Charsets;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
|
||||
|
||||
/****************************************************
|
||||
*
|
||||
* 微信支付的统一下单工具类
|
||||
*
|
||||
* @author majker
|
||||
* @version 1.0
|
||||
**************************************************/
|
||||
public class PaymentKit {
|
||||
|
||||
/**
|
||||
* 组装签名的字段
|
||||
* @param params 参数
|
||||
* @param urlEncoder 是否urlEncoder
|
||||
* @return String
|
||||
*/
|
||||
public static String packageSign(Map<String, String> params, boolean urlEncoder) {
|
||||
// 先将参数以其参数名的字典序升序进行排序
|
||||
TreeMap<String, String> sortedParams = new TreeMap<String, String>(params);
|
||||
// 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean first = true;
|
||||
for (Entry<String, String> param : sortedParams.entrySet()) {
|
||||
String value = param.getValue();
|
||||
if (Tools.isEmpty(value)) {
|
||||
continue;
|
||||
}
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
sb.append("&");
|
||||
}
|
||||
sb.append(param.getKey()).append("=");
|
||||
if (urlEncoder) {
|
||||
try { value = urlEncode(value); } catch (UnsupportedEncodingException e) {}
|
||||
}
|
||||
sb.append(value);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* urlEncode
|
||||
* @param src 微信参数
|
||||
* @return String
|
||||
* @throws UnsupportedEncodingException 编码错误
|
||||
*/
|
||||
public static String urlEncode(String src) throws UnsupportedEncodingException {
|
||||
return URLEncoder.encode(src, Charsets.UTF_8.name()).replace("+", "%20");
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名
|
||||
* @param params 参数
|
||||
* @param paternerKey 支付密钥
|
||||
* @return sign
|
||||
*/
|
||||
public static String createSign(Map<String, String> params, String paternerKey) {
|
||||
// 生成签名前先去除sign
|
||||
params.remove("sign");
|
||||
String stringA = packageSign(params, false);
|
||||
String stringSignTemp = stringA + "&key=" + paternerKey;
|
||||
return Tools.md5(stringSignTemp).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付异步通知时校验sign
|
||||
* @param params 参数
|
||||
* @param paternerKey 支付密钥
|
||||
* @return {boolean}
|
||||
*/
|
||||
public static boolean verifyNotify(Map<String, String> params, String paternerKey){
|
||||
String sign = params.get("sign");
|
||||
String localSign = PaymentKit.createSign(params, paternerKey);
|
||||
return sign.equals(localSign);
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信下单,map to xml
|
||||
* @param params 参数
|
||||
* @return String
|
||||
*/
|
||||
public static String toXml(Map<String, String> params) {
|
||||
StringBuilder xml = new StringBuilder();
|
||||
xml.append("<xml>");
|
||||
for (Entry<String, String> entry : params.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
// 略过空值
|
||||
if (Tools.isEmpty(value)) continue;
|
||||
xml.append("<").append(key).append(">");
|
||||
xml.append(entry.getValue());
|
||||
xml.append("</").append(key).append(">");
|
||||
}
|
||||
xml.append("</xml>");
|
||||
return xml.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 针对支付的xml,没有嵌套节点的简单处理
|
||||
* @param xmlStr xml字符串
|
||||
* @return map集合
|
||||
*/
|
||||
public static Map<String, String> xmlToMap(String xmlStr) {
|
||||
XmlHelper xmlHelper = XmlHelper.of(xmlStr);
|
||||
return xmlHelper.toMap();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
package co.yixiang.yshop.module.pay.strategy.weixin.skd;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @Name RandomStringUtil
|
||||
* @Descr 生成随机字符串
|
||||
* @author lnexin@aliyun.com
|
||||
* @date 2015年10月15日下午2:36:05
|
||||
*/
|
||||
public class RandomStringUtil {
|
||||
/**
|
||||
* @param passLength
|
||||
* : 要生成多少长度的字符串
|
||||
* @param type
|
||||
* : 需要哪种类型
|
||||
* @return 根据传入的type来判定
|
||||
*/
|
||||
|
||||
// 可以根据自己需求来删减下面的代码,不要要的类型可以删掉
|
||||
|
||||
// type=0:纯数字(0-9)
|
||||
// type=1:全小写字母(a-z)
|
||||
// type=2:全大写字母(A-Z)
|
||||
// type=3: 数字+小写字母
|
||||
// type=4: 数字+大写字母
|
||||
// type=5:大写字母+小写字母
|
||||
// type=6:数字+大写字母+小写字母
|
||||
// type=7:固定长度33位:根据UUID拿到的随机字符串,去掉了四个"-"(相当于长度33位的小写字母加数字)
|
||||
|
||||
public static String getRandomCode(int passLength, int type) {
|
||||
StringBuffer buffer = null;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
Random r = new Random();
|
||||
r.setSeed(new Date().getTime());
|
||||
switch (type) {
|
||||
case 0:
|
||||
buffer = new StringBuffer("123456789");
|
||||
break;
|
||||
case 1:
|
||||
buffer = new StringBuffer("abcdefghijklmnopqrstuvwxyz");
|
||||
break;
|
||||
case 2:
|
||||
buffer = new StringBuffer("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
||||
break;
|
||||
case 3:
|
||||
buffer = new StringBuffer("0123456789abcdefghijklmnopqrstuvwxyz");
|
||||
break;
|
||||
case 4:
|
||||
buffer = new StringBuffer("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
||||
break;
|
||||
case 5:
|
||||
buffer = new StringBuffer("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
||||
break;
|
||||
case 6:
|
||||
buffer = new StringBuffer("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
|
||||
sb.append(buffer.charAt(r.nextInt(buffer.length() - 10)));
|
||||
passLength -= 1;
|
||||
break;
|
||||
case 7:
|
||||
String s = UUID.randomUUID().toString();
|
||||
sb.append(s.substring(0, 8) + s.substring(9, 13) + s.substring(14, 18) + s.substring(19, 23) + s.substring(24));
|
||||
}
|
||||
|
||||
if (type != 7) {
|
||||
int range = buffer.length();
|
||||
for (int i = 0; i < passLength; ++i) {
|
||||
sb.append(buffer.charAt(r.nextInt(range)));
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
public static String getRandom() {
|
||||
String num = "";
|
||||
for (int i = 0; i < 4; i++) {
|
||||
num = num + String.valueOf((int) Math.floor(Math.random() * 9 + 1));
|
||||
}
|
||||
return num;
|
||||
}
|
||||
}
|
@ -0,0 +1,310 @@
|
||||
package co.yixiang.yshop.module.pay.strategy.weixin.skd;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/****************************************************
|
||||
*
|
||||
* 常用工具
|
||||
*
|
||||
* @author majker
|
||||
* @version 1.0
|
||||
**************************************************/
|
||||
public class Tools {
|
||||
|
||||
/**
|
||||
* 随机生成六位数验证码
|
||||
* @return
|
||||
*/
|
||||
public static int getRandomNum(){
|
||||
Random r = new Random();
|
||||
return r.nextInt(900000)+100000;//(Math.random()*(999999-100000)+100000)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测字符串是否不为空(null,"","null")
|
||||
* @param s
|
||||
* @return 不为空则返回true,否则返回false
|
||||
*/
|
||||
public static boolean notEmpty(String s){
|
||||
return s!=null && !"".equals(s) && !"null".equals(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测字符串是否为空(null,"","null")
|
||||
* @param s
|
||||
* @return 为空则返回true,不否则返回false
|
||||
*/
|
||||
public static boolean isEmpty(String s){
|
||||
return s==null || "".equals(s) || "null".equals(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串转换为字符串数组
|
||||
* @param str 字符串
|
||||
* @param splitRegex 分隔符
|
||||
* @return
|
||||
*/
|
||||
public static String[] str2StrArray(String str,String splitRegex){
|
||||
if(isEmpty(str)){
|
||||
return null;
|
||||
}
|
||||
return str.split(splitRegex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用默认的分隔符(,)将字符串转换为字符串数组
|
||||
* @param str 字符串
|
||||
* @return
|
||||
*/
|
||||
public static String[] str2StrArray(String str){
|
||||
return str2StrArray(str,",\\s*");
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照yyyy-MM-dd HH:mm:ss的格式,日期转字符串
|
||||
* @param date
|
||||
* @return yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
public static String date2Str(Date date){
|
||||
return date2Str(date,"yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照yyyy-MM-dd HH:mm:ss的格式,字符串转日期
|
||||
* @param date
|
||||
* @return
|
||||
*/
|
||||
public static Date str2Date(String date){
|
||||
if(notEmpty(date)){
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
try {
|
||||
return sdf.parse(date);
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return new Date();
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照参数format的格式,日期转字符串
|
||||
* @param date
|
||||
* @param format
|
||||
* @return
|
||||
*/
|
||||
public static String date2Str(Date date,String format){
|
||||
if(date!=null){
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(format);
|
||||
return sdf.format(date);
|
||||
}else{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 把时间根据时、分、秒转换为时间段
|
||||
* @param StrDate
|
||||
*/
|
||||
public static String getTimes(String StrDate){
|
||||
String resultTimes = "";
|
||||
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
Date now;
|
||||
|
||||
try {
|
||||
now = new Date();
|
||||
Date date=df.parse(StrDate);
|
||||
long times = now.getTime()-date.getTime();
|
||||
long day = times/(24*60*60*1000);
|
||||
long hour = (times/(60*60*1000)-day*24);
|
||||
long min = ((times/(60*1000))-day*24*60-hour*60);
|
||||
long sec = (times/1000-day*24*60*60-hour*60*60-min*60);
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
//sb.append("发表于:");
|
||||
if(hour>0 ){
|
||||
sb.append(hour+"小时前");
|
||||
} else if(min>0){
|
||||
sb.append(min+"分钟前");
|
||||
} else{
|
||||
sb.append(sec+"秒前");
|
||||
}
|
||||
|
||||
resultTimes = sb.toString();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return resultTimes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证邮箱
|
||||
* @param email
|
||||
* @return
|
||||
*/
|
||||
public static boolean checkEmail(String email){
|
||||
boolean flag = false;
|
||||
try{
|
||||
String check = "^([a-z0-9A-Z]+[-|_|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
|
||||
Pattern regex = Pattern.compile(check);
|
||||
Matcher matcher = regex.matcher(email);
|
||||
flag = matcher.matches();
|
||||
}catch(Exception e){
|
||||
flag = false;
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证手机号码
|
||||
* @return
|
||||
*/
|
||||
public static boolean checkMobileNumber(String mobileNumber){
|
||||
Pattern p = null;
|
||||
Matcher m = null;
|
||||
boolean b = false;
|
||||
p = Pattern.compile("^[1][3,4,5,7,8][0-9]{9}$"); // 验证手机号
|
||||
m = p.matcher(mobileNumber);
|
||||
b = m.matches();
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将驼峰转下划线
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
public static String camelToUnderline(String param){
|
||||
if (param==null||"".equals(param.trim())){
|
||||
return "";
|
||||
}
|
||||
int len=param.length();
|
||||
StringBuilder sb=new StringBuilder(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c=param.charAt(i);
|
||||
if (Character.isUpperCase(c)){
|
||||
sb.append("_");
|
||||
sb.append(Character.toLowerCase(c));
|
||||
}else{
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 去掉下划线并将下划线后的首字母转为大写
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static String transformStr(String str){
|
||||
//去掉数据库字段的下划线
|
||||
if(str.contains("_")) {
|
||||
String[] names = str.split("_");
|
||||
String firstPart = names[0];
|
||||
String otherPart = "";
|
||||
for (int i = 1; i < names.length; i++) {
|
||||
String word = names[i].replaceFirst(names[i].substring(0, 1), names[i].substring(0, 1).toUpperCase());
|
||||
otherPart += word;
|
||||
}
|
||||
str = firstPart + otherPart;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为map
|
||||
* @param list
|
||||
* @return
|
||||
*/
|
||||
public static List<Map<String,Object>> transformMap(List<Map<String,Object>> list){
|
||||
List<Map<String,Object>> resultMapList = new ArrayList<>();
|
||||
|
||||
for (Map<String, Object> map : list) {
|
||||
Map<String,Object> tempMap = new HashMap<>();
|
||||
for (String s : map.keySet()) {
|
||||
tempMap.put(transformStr(s),map.get(s));
|
||||
}
|
||||
resultMapList.add(tempMap);
|
||||
}
|
||||
return resultMapList;
|
||||
}
|
||||
|
||||
public static String clearHtml(String content,int p) {
|
||||
if(null==content) return "";
|
||||
if(0==p) return "";
|
||||
|
||||
Pattern p_script;
|
||||
Matcher m_script;
|
||||
Pattern p_style;
|
||||
Matcher m_style;
|
||||
Pattern p_html;
|
||||
Matcher m_html;
|
||||
|
||||
try {
|
||||
String regEx_script = "<[\\s]*?script[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?script[\\s]*?>";
|
||||
//定义script的正则表达式{或<script[^>]*?>[\\s\\S]*?<\\/script> }
|
||||
String regEx_style = "<[\\s]*?style[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?style[\\s]*?>";
|
||||
//定义style的正则表达式{或<style[^>]*?>[\\s\\S]*?<\\/style> }
|
||||
String regEx_html = "<[^>]+>"; //定义HTML标签的正则表达式
|
||||
|
||||
p_script = Pattern.compile(regEx_script,Pattern.CASE_INSENSITIVE);
|
||||
m_script = p_script.matcher(content);
|
||||
content = m_script.replaceAll(""); //过滤script标签
|
||||
p_style = Pattern.compile(regEx_style,Pattern.CASE_INSENSITIVE);
|
||||
m_style = p_style.matcher(content);
|
||||
content = m_style.replaceAll(""); //过滤style标签
|
||||
|
||||
p_html = Pattern.compile(regEx_html,Pattern.CASE_INSENSITIVE);
|
||||
m_html = p_html.matcher(content);
|
||||
|
||||
content = m_html.replaceAll(""); //过滤html标签
|
||||
}catch(Exception e) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if(content.length()>p){
|
||||
content = content.substring(0, p)+"...";
|
||||
}else{
|
||||
content = content + "...";
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
public static String md5(String str) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
md.update(str.getBytes());
|
||||
byte b[] = md.digest();
|
||||
|
||||
int i;
|
||||
|
||||
StringBuffer buf = new StringBuffer("");
|
||||
for (int offset = 0; offset < b.length; offset++) {
|
||||
i = b[offset];
|
||||
if (i < 0)
|
||||
i += 256;
|
||||
if (i < 16)
|
||||
buf.append("0");
|
||||
buf.append(Integer.toHexString(i));
|
||||
}
|
||||
str = buf.toString();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
package co.yixiang.yshop.module.pay.strategy.weixin.skd;
|
||||
|
||||
/**
|
||||
* 常量
|
||||
*/
|
||||
public class WXPayConstants {
|
||||
|
||||
public enum SignType {
|
||||
MD5, HMACSHA256
|
||||
}
|
||||
|
||||
public static final String DOMAIN_API = "api.mch.wechat.qq.com";
|
||||
public static final String DOMAIN_API2 = "api2.mch.wechat.qq.com";
|
||||
public static final String DOMAIN_APIHK = "apihk.mch.wechat.qq.com";
|
||||
public static final String DOMAIN_APIUS = "apius.mch.wechat.qq.com";
|
||||
|
||||
|
||||
public static final String FAIL = "FAIL";
|
||||
public static final String SUCCESS = "SUCCESS";
|
||||
public static final String HMACSHA256 = "HMAC-SHA256";
|
||||
public static final String MD5 = "MD5";
|
||||
|
||||
public static final String FIELD_SIGN = "sign";
|
||||
public static final String FIELD_SIGN_TYPE = "sign_type";
|
||||
|
||||
public static final String MICROPAY_URL_SUFFIX = "/pay/micropay";
|
||||
public static final String UNIFIEDORDER_URL_SUFFIX = "/pay/unifiedorder";
|
||||
public static final String ORDERQUERY_URL_SUFFIX = "/pay/orderquery";
|
||||
public static final String REVERSE_URL_SUFFIX = "/secapi/pay/reverse";
|
||||
public static final String CLOSEORDER_URL_SUFFIX = "/pay/closeorder";
|
||||
public static final String REFUND_URL_SUFFIX = "/secapi/pay/refund";
|
||||
public static final String REFUNDQUERY_URL_SUFFIX = "/pay/refundquery";
|
||||
public static final String DOWNLOADBILL_URL_SUFFIX = "/pay/downloadbill";
|
||||
public static final String REPORT_URL_SUFFIX = "/payitil/report";
|
||||
public static final String SHORTURL_URL_SUFFIX = "/tools/shorturl";
|
||||
public static final String AUTHCODETOOPENID_URL_SUFFIX = "/tools/authcodetoopenid";
|
||||
|
||||
// sandbox
|
||||
public static final String SANDBOX_MICROPAY_URL_SUFFIX = "/sandboxnew/pay/micropay";
|
||||
public static final String SANDBOX_UNIFIEDORDER_URL_SUFFIX = "/sandboxnew/pay/unifiedorder";
|
||||
public static final String SANDBOX_ORDERQUERY_URL_SUFFIX = "/sandboxnew/pay/orderquery";
|
||||
public static final String SANDBOX_REVERSE_URL_SUFFIX = "/sandboxnew/secapi/pay/reverse";
|
||||
public static final String SANDBOX_CLOSEORDER_URL_SUFFIX = "/sandboxnew/pay/closeorder";
|
||||
public static final String SANDBOX_REFUND_URL_SUFFIX = "/sandboxnew/secapi/pay/refund";
|
||||
public static final String SANDBOX_REFUNDQUERY_URL_SUFFIX = "/sandboxnew/pay/refundquery";
|
||||
public static final String SANDBOX_DOWNLOADBILL_URL_SUFFIX = "/sandboxnew/pay/downloadbill";
|
||||
public static final String SANDBOX_REPORT_URL_SUFFIX = "/sandboxnew/payitil/report";
|
||||
public static final String SANDBOX_SHORTURL_URL_SUFFIX = "/sandboxnew/tools/shorturl";
|
||||
public static final String SANDBOX_AUTHCODETOOPENID_URL_SUFFIX = "/sandboxnew/tools/authcodetoopenid";
|
||||
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,366 @@
|
||||
package co.yixiang.yshop.module.pay.strategy.weixin.skd;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.ssl.SSLContexts;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.security.KeyStore;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.*;
|
||||
|
||||
/****************************************************
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author majker
|
||||
* @date: 2019/3/7
|
||||
* @version 1.0
|
||||
**************************************************/
|
||||
@Slf4j
|
||||
public class WXPayUtil {
|
||||
|
||||
/**
|
||||
* XML格式字符串转换为Map
|
||||
*
|
||||
* @param strXML XML字符串
|
||||
* @return XML数据转换后的Map
|
||||
* @throws Exception
|
||||
*/
|
||||
public static Map<String, String> xmlToMap(String strXML) throws Exception {
|
||||
try {
|
||||
Map<String, String> data = new HashMap<String, String>();
|
||||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
|
||||
InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
|
||||
org.w3c.dom.Document doc = documentBuilder.parse(stream);
|
||||
doc.getDocumentElement().normalize();
|
||||
NodeList nodeList = doc.getDocumentElement().getChildNodes();
|
||||
for (int idx = 0; idx < nodeList.getLength(); ++idx) {
|
||||
Node node = nodeList.item(idx);
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
|
||||
data.put(element.getNodeName(), element.getTextContent());
|
||||
}
|
||||
}
|
||||
try {
|
||||
stream.close();
|
||||
} catch (Exception ex) {
|
||||
// do nothing
|
||||
}
|
||||
return data;
|
||||
} catch (Exception ex) {
|
||||
WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Map转换为XML格式的字符串
|
||||
*
|
||||
* @param data Map类型数据
|
||||
* @return XML格式的字符串
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String mapToXml(Map<String, String> data) throws Exception {
|
||||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
|
||||
org.w3c.dom.Document document = documentBuilder.newDocument();
|
||||
org.w3c.dom.Element root = document.createElement("xml");
|
||||
document.appendChild(root);
|
||||
for (String key: data.keySet()) {
|
||||
String value = data.get(key);
|
||||
if (value == null) {
|
||||
value = "";
|
||||
}
|
||||
value = value.trim();
|
||||
org.w3c.dom.Element filed = document.createElement(key);
|
||||
filed.appendChild(document.createTextNode(value));
|
||||
root.appendChild(filed);
|
||||
}
|
||||
TransformerFactory tf = TransformerFactory.newInstance();
|
||||
Transformer transformer = tf.newTransformer();
|
||||
DOMSource source = new DOMSource(document);
|
||||
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
|
||||
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||
StringWriter writer = new StringWriter();
|
||||
StreamResult result = new StreamResult(writer);
|
||||
transformer.transform(source, result);
|
||||
String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
|
||||
try {
|
||||
writer.close();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成带有 sign 的 XML 格式字符串
|
||||
*
|
||||
* @param data Map类型数据
|
||||
* @param key API密钥
|
||||
* @return 含有sign字段的XML
|
||||
*/
|
||||
public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {
|
||||
return generateSignedXml(data, key, WXPayConstants.SignType.MD5);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成带有 sign 的 XML 格式字符串
|
||||
*
|
||||
* @param data Map类型数据
|
||||
* @param key API密钥
|
||||
* @param signType 签名类型
|
||||
* @return 含有sign字段的XML
|
||||
*/
|
||||
public static String generateSignedXml(final Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
|
||||
String sign = generateSignature(data, key, signType);
|
||||
data.put(WXPayConstants.FIELD_SIGN, sign);
|
||||
return mapToXml(data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断签名是否正确
|
||||
*
|
||||
* @param xmlStr XML格式数据
|
||||
* @param key API密钥
|
||||
* @return 签名是否正确
|
||||
* @throws Exception
|
||||
*/
|
||||
public static boolean isSignatureValid(String xmlStr, String key) throws Exception {
|
||||
Map<String, String> data = xmlToMap(xmlStr);
|
||||
if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
|
||||
return false;
|
||||
}
|
||||
String sign = data.get(WXPayConstants.FIELD_SIGN);
|
||||
return generateSignature(data, key).equals(sign);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。
|
||||
*
|
||||
* @param data Map类型数据
|
||||
* @param key API密钥
|
||||
* @return 签名是否正确
|
||||
* @throws Exception
|
||||
*/
|
||||
public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
|
||||
return isSignatureValid(data, key, WXPayConstants.SignType.MD5);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断签名是否正确,必须包含sign字段,否则返回false。
|
||||
*
|
||||
* @param data Map类型数据
|
||||
* @param key API密钥
|
||||
* @param signType 签名方式
|
||||
* @return 签名是否正确
|
||||
* @throws Exception
|
||||
*/
|
||||
public static boolean isSignatureValid(Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
|
||||
if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
|
||||
return false;
|
||||
}
|
||||
String sign = data.get(WXPayConstants.FIELD_SIGN);
|
||||
return generateSignature(data, key, signType).equals(sign);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名
|
||||
*
|
||||
* @param data 待签名数据
|
||||
* @param key API密钥
|
||||
* @return 签名
|
||||
*/
|
||||
public static String generateSignature(final Map<String, String> data, String key) throws Exception {
|
||||
return generateSignature(data, key, WXPayConstants.SignType.MD5);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
|
||||
*
|
||||
* @param data 待签名数据
|
||||
* @param key API密钥
|
||||
* @param signType 签名方式
|
||||
* @return 签名
|
||||
*/
|
||||
public static String generateSignature(final Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
|
||||
Set<String> keySet = data.keySet();
|
||||
String[] keyArray = keySet.toArray(new String[0]);
|
||||
Arrays.sort(keyArray);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String k : keyArray) {
|
||||
if (k.equals(WXPayConstants.FIELD_SIGN)) {
|
||||
continue;
|
||||
}
|
||||
if(data.get(k).trim().length() > 0) { // 参数值为空,则不参与签名
|
||||
sb.append(k).append("=").append(data.get(k).trim()).append("&");
|
||||
}
|
||||
}
|
||||
sb.append("key=").append(key);
|
||||
if (WXPayConstants.SignType.MD5.equals(signType)) {
|
||||
return MD5(sb.toString()).toUpperCase();
|
||||
}
|
||||
else if (WXPayConstants.SignType.HMACSHA256.equals(signType)) {
|
||||
return HMACSHA256(sb.toString(), key);
|
||||
}
|
||||
else {
|
||||
throw new Exception(String.format("Invalid sign_type: %s", signType));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取随机字符串 Nonce Str
|
||||
*
|
||||
* @return String 随机字符串
|
||||
*/
|
||||
public static String generateNonceStr() {
|
||||
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成 MD5
|
||||
*
|
||||
* @param data 待处理数据
|
||||
* @return MD5结果
|
||||
*/
|
||||
public static String MD5(String data) throws Exception {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] array = md.digest(data.getBytes("UTF-8"));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte item : array) {
|
||||
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
|
||||
}
|
||||
return sb.toString().toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 HMACSHA256
|
||||
* @param data 待处理数据
|
||||
* @param key 密钥
|
||||
* @return 加密结果
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String HMACSHA256(String data, String key) throws Exception {
|
||||
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
|
||||
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
|
||||
sha256_HMAC.init(secret_key);
|
||||
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte item : array) {
|
||||
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
|
||||
}
|
||||
return sb.toString().toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志
|
||||
* @return
|
||||
*/
|
||||
public static Logger getLogger() {
|
||||
Logger logger = LoggerFactory.getLogger("wxpay java sdk");
|
||||
return logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前时间戳,单位秒
|
||||
* @return
|
||||
*/
|
||||
public static long getCurrentTimestamp() {
|
||||
return System.currentTimeMillis()/1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前时间戳,单位毫秒
|
||||
* @return
|
||||
*/
|
||||
public static long getCurrentTimestampMs() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 uuid, 即用来标识一笔单,也用做 nonce_str
|
||||
* @return
|
||||
*/
|
||||
public static String generateUUID() {
|
||||
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* 申请退款
|
||||
*/
|
||||
public static String doRefund(String url, String data,String cert_url,String mch_id) throws Exception {
|
||||
KeyStore keyStore = KeyStore.getInstance("PKCS12");
|
||||
File file = ResourceUtils.getFile(cert_url);
|
||||
// FileInputStream is = new FileInputStream(file);
|
||||
try (InputStream is = Files.newInputStream(file.toPath())) {
|
||||
keyStore.load(is, mch_id.toCharArray());
|
||||
}
|
||||
// Trust own CA and all self-signed certs
|
||||
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(
|
||||
keyStore,
|
||||
mch_id.toCharArray())
|
||||
.build();
|
||||
// Allow TLSv1 protocol only
|
||||
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
|
||||
sslcontext,
|
||||
// new String[]{"TLSv1"},
|
||||
null,
|
||||
null,
|
||||
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
|
||||
);
|
||||
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
|
||||
try {
|
||||
HttpPost httpost = new HttpPost(url); // 设置响应头信息
|
||||
// httpost.addHeader("Connection", "keep-alive");
|
||||
// httpost.addHeader("Accept", "*/*");
|
||||
// httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
|
||||
// httpost.addHeader("Host", "api.mch.wechat.qq.com");
|
||||
// httpost.addHeader("X-Requested-With", "XMLHttpRequest");
|
||||
// httpost.addHeader("Cache-Control", "max-age=0");
|
||||
// httpost.addHeader("ElectronicUser-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
|
||||
httpost.setEntity(new StringEntity(data, "UTF-8"));
|
||||
CloseableHttpResponse response = httpclient.execute(httpost);
|
||||
try {
|
||||
HttpEntity entity = response.getEntity();
|
||||
String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
|
||||
EntityUtils.consume(entity);
|
||||
return jsonStr;
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
} finally {
|
||||
httpclient.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,359 @@
|
||||
package co.yixiang.yshop.module.pay.strategy.weixin.skd; /**
|
||||
* Create by eval on 2019/3/20
|
||||
*/
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @ClassName WechatUtil
|
||||
* @Description TODO
|
||||
* @Author eval
|
||||
* @Date 9:44 2019/3/20
|
||||
* @Version 1.0
|
||||
*/
|
||||
@Slf4j(topic = "WechatUtil")
|
||||
@Component
|
||||
public class WechatUtil {
|
||||
|
||||
/**
|
||||
* 支付小程序appid
|
||||
*/
|
||||
private static String appId;
|
||||
|
||||
/**
|
||||
* 支付小程序秘钥
|
||||
*/
|
||||
private static String secret;
|
||||
|
||||
@Value("${weixin.appid}")
|
||||
public void setAppId(String appId){
|
||||
this.appId = appId;
|
||||
}
|
||||
|
||||
@Value("${weixin.secret}")
|
||||
public void setSecret(String secret){
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取小程序codeid换取openid
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
public static JSONObject getOpenId(String code) {
|
||||
String url = "https://api.weixin.qq.com/sns/jscode2session?appid="+ appId +"&secret=" +
|
||||
secret +"&js_code="+code+"&grant_type=authorization_code";
|
||||
PrintWriter out = null;
|
||||
BufferedReader in = null;
|
||||
String line;
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
try {
|
||||
URL realUrl = new URL(url);
|
||||
// 打开和URL之间的连接
|
||||
URLConnection conn = realUrl.openConnection();
|
||||
|
||||
// 设置通用的请求属性 设置请求格式
|
||||
//设置返回类型
|
||||
conn.setRequestProperty("contentType", "text/plain");
|
||||
//设置请求类型
|
||||
conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
|
||||
//设置超时时间
|
||||
conn.setConnectTimeout(1000);
|
||||
conn.setReadTimeout(1000);
|
||||
conn.setDoOutput(true);
|
||||
conn.connect();
|
||||
// 获取URLConnection对象对应的输出流
|
||||
out = new PrintWriter(conn.getOutputStream());
|
||||
// flush输出流的缓冲
|
||||
out.flush();
|
||||
// 定义BufferedReader输入流来读取URL的响应 设置接收格式
|
||||
in = new BufferedReader(
|
||||
new InputStreamReader(conn.getInputStream(), "UTF-8"));
|
||||
while ((line = in.readLine()) != null) {
|
||||
stringBuffer.append(line);
|
||||
}
|
||||
JSONObject jsonObject = JSONObject.parseObject(stringBuffer.toString());
|
||||
return jsonObject;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//使用finally块来关闭输出流、输入流
|
||||
finally {
|
||||
try {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取小程序token和openid
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
public static JSONObject getAccessTokenOrOpenid(String code) {
|
||||
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
|
||||
"appid="+ appId +"&secret="+ secret +"&code="+code+"&grant_type=authorization_code";
|
||||
PrintWriter out = null;
|
||||
BufferedReader in = null;
|
||||
String line;
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
try {
|
||||
URL realUrl = new URL(url);
|
||||
// 打开和URL之间的连接
|
||||
URLConnection conn = realUrl.openConnection();
|
||||
|
||||
// 设置通用的请求属性 设置请求格式
|
||||
//设置返回类型
|
||||
conn.setRequestProperty("contentType", "text/plain");
|
||||
//设置请求类型
|
||||
conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
|
||||
//设置超时时间
|
||||
conn.setConnectTimeout(1000);
|
||||
conn.setReadTimeout(1000);
|
||||
conn.setDoOutput(true);
|
||||
conn.connect();
|
||||
// 获取URLConnection对象对应的输出流
|
||||
out = new PrintWriter(conn.getOutputStream());
|
||||
// flush输出流的缓冲
|
||||
out.flush();
|
||||
// 定义BufferedReader输入流来读取URL的响应 设置接收格式
|
||||
in = new BufferedReader(
|
||||
new InputStreamReader(conn.getInputStream(), "UTF-8"));
|
||||
while ((line = in.readLine()) != null) {
|
||||
stringBuffer.append(line);
|
||||
}
|
||||
JSONObject jsonObject = JSONObject.parseObject(stringBuffer.toString());
|
||||
return jsonObject;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//使用finally块来关闭输出流、输入流
|
||||
finally {
|
||||
try {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取小程序unionId
|
||||
* @return
|
||||
*/
|
||||
public static String getUnionId(String accessToken,String openid) {
|
||||
String url = "https://api.weixin.qq.com/sns/userinfo?access_token="+accessToken+"&openid="+openid;
|
||||
PrintWriter out = null;
|
||||
BufferedReader in = null;
|
||||
String line;
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
try {
|
||||
URL realUrl = new URL(url);
|
||||
// 打开和URL之间的连接
|
||||
URLConnection conn = realUrl.openConnection();
|
||||
|
||||
// 设置通用的请求属性 设置请求格式
|
||||
//设置返回类型
|
||||
conn.setRequestProperty("contentType", "text/plain");
|
||||
//设置请求类型
|
||||
conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
|
||||
//设置超时时间
|
||||
conn.setConnectTimeout(1000);
|
||||
conn.setReadTimeout(1000);
|
||||
conn.setDoOutput(true);
|
||||
conn.connect();
|
||||
// 获取URLConnection对象对应的输出流
|
||||
out = new PrintWriter(conn.getOutputStream());
|
||||
// flush输出流的缓冲
|
||||
out.flush();
|
||||
// 定义BufferedReader输入流来读取URL的响应 设置接收格式
|
||||
in = new BufferedReader(
|
||||
new InputStreamReader(conn.getInputStream(), "UTF-8"));
|
||||
while ((line = in.readLine()) != null) {
|
||||
stringBuffer.append(line);
|
||||
}
|
||||
JSONObject jsonObject = JSONObject.parseObject(stringBuffer.toString());
|
||||
return jsonObject.get("unionId").toString();
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//使用finally块来关闭输出流、输入流
|
||||
finally {
|
||||
try {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// public static Map<String, Object> getPhoneNumber(BuyerUserVo vo) {
|
||||
// Map<String,Object> map=new HashMap<>();
|
||||
// String openid= vo.getWechatOpenId();
|
||||
// String session_key = vo.getSessionKey();
|
||||
// if (!EmptyUtils.isEmpty(openid)) {
|
||||
//
|
||||
// if(EmptyUtils.isEmpty(session_key)){
|
||||
// return null;
|
||||
// }
|
||||
// map.put("openid",openid);
|
||||
// // 被加密的数据
|
||||
// byte[] dataByte = Base64.decode(vo.getEncryptedData());
|
||||
// // 加密秘钥
|
||||
// byte[] keyByte = Base64.decode(session_key);
|
||||
// // 偏移量
|
||||
// byte[] ivByte = Base64.decode(vo.getIv());
|
||||
// try {
|
||||
// // 如果密钥不足16位,那么就补足. 这个if 中的内容很重要
|
||||
// int base = 16;
|
||||
// if (keyByte.length % base != 0) {
|
||||
// int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
|
||||
// byte[] temp = new byte[groups * base];
|
||||
// Arrays.fill(temp, (byte) 0);
|
||||
// System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
|
||||
// keyByte = temp;
|
||||
// }
|
||||
// // 初始化
|
||||
// Security.addProvider(new BouncyCastleProvider());
|
||||
// Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
// SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
|
||||
// AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
|
||||
// IvParameterSpec ivParameterSpec=new IvParameterSpec(ivByte);
|
||||
// parameters.init(ivParameterSpec);
|
||||
// cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
|
||||
// byte[] resultByte = cipher.doFinal(dataByte);
|
||||
// if (null != resultByte && resultByte.length > 0) {
|
||||
// String result = new String(resultByte, "UTF-8");
|
||||
// JSONObject jsonObject = JSONObject.parseObject(result);
|
||||
// map.put("param",jsonObject);
|
||||
// return map;
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
|
||||
/**
|
||||
* 获取微信小程序 session_key 和 openid
|
||||
*
|
||||
* @param code 调用微信登陆返回的Code
|
||||
* @return
|
||||
*/
|
||||
public static JSONObject getSessionKeyOropenid(String code) {
|
||||
//微信端登录code值
|
||||
String wxCode = code;
|
||||
String requestUrl = "https://api.weixin.qq.com/sns/jscode2session"; //请求地址 https://api.weixin.qq.com/sns/jscode2session
|
||||
Map<String, String> requestUrlParam = new HashMap<String, String>();
|
||||
requestUrlParam.put("appid", appId); //开发者设置中的appId
|
||||
requestUrlParam.put("secret", secret); //开发者设置中的appSecret
|
||||
requestUrlParam.put("js_code", wxCode); //小程序调用wx.login返回的code
|
||||
requestUrlParam.put("grant_type", "authorization_code"); //默认参数 authorization_code
|
||||
|
||||
//发送post请求读取调用微信 https://api.weixin.qq.com/sns/jscode2session 接口获取openid用户唯一标识
|
||||
JSONObject jsonObject = JSON.parseObject(sendPost(requestUrl, requestUrlParam));
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* 向指定 URL 发送POST方法的请求
|
||||
*
|
||||
* @param url 发送请求的 URL
|
||||
* @return 所代表远程资源的响应结果
|
||||
*/
|
||||
public static String sendPost(String url, Map<String, ?> paramMap) {
|
||||
PrintWriter out = null;
|
||||
BufferedReader in = null;
|
||||
String result = "";
|
||||
|
||||
String param = "";
|
||||
Iterator<String> it = paramMap.keySet().iterator();
|
||||
|
||||
while (it.hasNext()) {
|
||||
String key = it.next();
|
||||
param += key + "=" + paramMap.get(key) + "&";
|
||||
}
|
||||
|
||||
try {
|
||||
URL realUrl = new URL(url);
|
||||
// 打开和URL之间的连接
|
||||
URLConnection conn = realUrl.openConnection();
|
||||
// 设置通用的请求属性
|
||||
conn.setRequestProperty("accept", "*/*");
|
||||
conn.setRequestProperty("connection", "Keep-Alive");
|
||||
conn.setRequestProperty("Accept-Charset", "utf-8");
|
||||
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
|
||||
// 发送POST请求必须设置如下两行
|
||||
conn.setDoOutput(true);
|
||||
conn.setDoInput(true);
|
||||
// 获取URLConnection对象对应的输出流
|
||||
out = new PrintWriter(conn.getOutputStream());
|
||||
// 发送请求参数
|
||||
out.print(param);
|
||||
// flush输出流的缓冲
|
||||
out.flush();
|
||||
// 定义BufferedReader输入流来读取URL的响应
|
||||
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
|
||||
String line;
|
||||
while ((line = in.readLine()) != null) {
|
||||
result += line;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
//使用finally块来关闭输出流、输入流
|
||||
finally {
|
||||
try {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,209 @@
|
||||
package co.yixiang.yshop.module.pay.strategy.weixin.skd;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* xpath解析xml
|
||||
* <pre>
|
||||
* 文档地址:
|
||||
* http://www.w3school.com.cn/xpath/index.asp
|
||||
* </pre>
|
||||
* @author majker
|
||||
*/
|
||||
public class XmlHelper {
|
||||
private final XPath path;
|
||||
private final Document doc;
|
||||
|
||||
private XmlHelper(InputSource inputSource) throws ParserConfigurationException, SAXException, IOException {
|
||||
DocumentBuilderFactory dbf = getDocumentBuilderFactory();
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
doc = db.parse(inputSource);
|
||||
path = getXPathFactory().newXPath();
|
||||
}
|
||||
|
||||
private static XmlHelper create(InputSource inputSource) {
|
||||
try {
|
||||
return new XmlHelper(inputSource);
|
||||
} catch (ParserConfigurationException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (SAXException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static XmlHelper of(InputStream is) {
|
||||
InputSource inputSource = new InputSource(is);
|
||||
return create(inputSource);
|
||||
}
|
||||
|
||||
public static XmlHelper of(String xmlStr) {
|
||||
StringReader sr = new StringReader(xmlStr.trim());
|
||||
InputSource inputSource = new InputSource(sr);
|
||||
XmlHelper xmlHelper = create(inputSource);
|
||||
IOUtils.closeQuietly(sr);
|
||||
return xmlHelper;
|
||||
}
|
||||
|
||||
private Object evalXPath(String expression, Object item, QName returnType) {
|
||||
item = null == item ? doc : item;
|
||||
try {
|
||||
return path.evaluate(expression, item, returnType);
|
||||
} catch (XPathExpressionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取String
|
||||
* @param expression 路径
|
||||
* @return String
|
||||
*/
|
||||
public String getString(String expression) {
|
||||
return (String) evalXPath(expression, null, XPathConstants.STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Boolean
|
||||
* @param expression 路径
|
||||
* @return String
|
||||
*/
|
||||
public Boolean getBoolean(String expression) {
|
||||
return (Boolean) evalXPath(expression, null, XPathConstants.BOOLEAN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Number
|
||||
* @param expression 路径
|
||||
* @return {Number}
|
||||
*/
|
||||
public Number getNumber(String expression) {
|
||||
return (Number) evalXPath(expression, null, XPathConstants.NUMBER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某个节点
|
||||
* @param expression 路径
|
||||
* @return {Node}
|
||||
*/
|
||||
public Node getNode(String expression) {
|
||||
return (Node) evalXPath(expression, null, XPathConstants.NODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子节点
|
||||
* @param expression 路径
|
||||
* @return NodeList
|
||||
*/
|
||||
public NodeList getNodeList(String expression) {
|
||||
return (NodeList) evalXPath(expression, null, XPathConstants.NODESET);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取String
|
||||
* @param node 节点
|
||||
* @param expression 相对于node的路径
|
||||
* @return String
|
||||
*/
|
||||
public String getString(Object node, String expression) {
|
||||
return (String) evalXPath(expression, node, XPathConstants.STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取
|
||||
* @param node 节点
|
||||
* @param expression 相对于node的路径
|
||||
* @return String
|
||||
*/
|
||||
public Boolean getBoolean(Object node, String expression) {
|
||||
return (Boolean) evalXPath(expression, node, XPathConstants.BOOLEAN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取
|
||||
* @param node 节点
|
||||
* @param expression 相对于node的路径
|
||||
* @return {Number}
|
||||
*/
|
||||
public Number getNumber(Object node, String expression) {
|
||||
return (Number) evalXPath(expression, node, XPathConstants.NUMBER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某个节点
|
||||
* @param node 节点
|
||||
* @param expression 路径
|
||||
* @return {Node}
|
||||
*/
|
||||
public Node getNode(Object node, String expression) {
|
||||
return (Node) evalXPath(expression, node, XPathConstants.NODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子节点
|
||||
* @param node 节点
|
||||
* @param expression 相对于node的路径
|
||||
* @return NodeList
|
||||
*/
|
||||
public NodeList getNodeList(Object node, String expression) {
|
||||
return (NodeList) evalXPath(expression, node, XPathConstants.NODESET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 针对没有嵌套节点的简单处理
|
||||
* @return map集合
|
||||
*/
|
||||
public Map<String, String> toMap() {
|
||||
Element root = doc.getDocumentElement();
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
|
||||
// 将节点封装成map形式
|
||||
NodeList list = root.getChildNodes();
|
||||
for (int i = 0; i < list.getLength(); i++) {
|
||||
Node node = list.item(i);
|
||||
if (node instanceof Element) {
|
||||
params.put(node.getNodeName(), node.getTextContent());
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
private static DocumentBuilderFactory getDocumentBuilderFactory(){
|
||||
return XmlHelperHolder.documentBuilderFactory;
|
||||
}
|
||||
|
||||
private static XPathFactory getXPathFactory() {
|
||||
return XmlHelperHolder.xPathFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部类单例
|
||||
*/
|
||||
private static class XmlHelperHolder {
|
||||
private static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
private static XPathFactory xPathFactory = XPathFactory.newInstance();
|
||||
}
|
||||
|
||||
}
|
||||
|
Binary file not shown.
Reference in New Issue
Block a user