package com.sinata.rest.common;
|
|
import com.alipay.api.AlipayApiException;
|
import com.alipay.api.AlipayClient;
|
import com.alipay.api.DefaultAlipayClient;
|
import com.alipay.api.domain.AlipayFundTransToaccountTransferModel;
|
import com.alipay.api.domain.AlipayTradeAppPayModel;
|
import com.alipay.api.domain.AlipayTradeQueryModel;
|
import com.alipay.api.domain.AlipayTradeWapPayModel;
|
import com.alipay.api.internal.util.AlipaySignature;
|
import com.alipay.api.request.AlipayTradeQueryRequest;
|
import com.alipay.api.request.AlipayTradeRefundRequest;
|
import com.alipay.api.request.AlipayTradeWapPayRequest;
|
import com.alipay.api.response.AlipayTradeQueryResponse;
|
import com.alipay.api.response.AlipayTradeRefundResponse;
|
import com.jpay.alipay.AliPayApi;
|
import com.jpay.alipay.AliPayApiConfig;
|
import com.jpay.alipay.AliPayApiConfigKit;
|
import com.jpay.ext.kit.PaymentKit;
|
import com.jpay.weixin.api.WxPayApi;
|
import com.sinata.rest.common.model.WxPay;
|
import com.sinata.rest.common.model.WxPayResult;
|
import com.sinata.rest.common.model.WxSignVO;
|
import com.sinata.rest.config.properties.PayProperties;
|
import com.sinata.rest.core.util.ToolUtil;
|
import lombok.extern.slf4j.Slf4j;
|
import net.sf.json.JSONObject;
|
import org.springframework.context.ApplicationContext;
|
import org.springframework.util.Assert;
|
import org.springframework.util.ClassUtils;
|
import org.springframework.util.StringUtils;
|
|
import javax.servlet.http.HttpServletRequest;
|
import java.io.InputStream;
|
import java.io.UnsupportedEncodingException;
|
import java.math.BigDecimal;
|
import java.net.HttpURLConnection;
|
import java.net.URL;
|
import java.net.URLEncoder;
|
import java.security.MessageDigest;
|
import java.security.NoSuchAlgorithmException;
|
import java.util.*;
|
|
@Slf4j
|
public class PayUtils {
|
|
private static PayProperties payProperties;
|
|
static {
|
ApplicationContext applicationContext = SpringUtil.getApplicationContext();
|
payProperties = (PayProperties) applicationContext.getBean("payProperties");
|
}
|
|
public static class PayResult {
|
private String msg;
|
private boolean success = false;
|
private String url;
|
private WxSignVO vo;
|
|
public String getMsg() {
|
return msg;
|
}
|
|
public boolean isSuccess() {
|
return success;
|
}
|
|
public void debug() {
|
this.success = true;
|
}
|
|
public String getUrl() {
|
return this.url;
|
}
|
|
public WxSignVO getVo() {
|
return vo;
|
}
|
|
public void setVo(WxSignVO vo) {
|
this.vo = vo;
|
}
|
|
}
|
|
// 算法名
|
private static final String KEY_NAME = "AES";
|
// 加解密算法/模式/填充方式
|
// ECB模式只用密钥即可对数据进行加密解密,CBC模式需要添加一个iv
|
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";
|
|
private static final String PRIVATE_KEY = payProperties.getAliPrivateKey();
|
public static final String ALI_PUBLIC_KEY = payProperties.getAliPublicKey();
|
private static final String ALI_APP_ID = payProperties.getAliAppId();
|
private static final String aliNotifyUrl = payProperties.getAliNotifyUrl();
|
private static final String aliReturnUrl = payProperties.getAliReturnUrl();
|
private static final String aliId = payProperties.getAliSellerId();
|
private static AlipayClient alipayClient = null;
|
|
//微信appId
|
private static final String wxAppId = payProperties.getWxAppId();
|
// 微信服务号ID
|
private static final String wxServiceAppId = payProperties.getWxServiceAppId();
|
//微信商户id
|
private static final String wxMchId = payProperties.getWxMchId();
|
//微信支付回调地址
|
private static final String notifyUrl = payProperties.getWxNotifyUrl();
|
//微信SecretKey
|
private static final String secretKey = payProperties.getWxAppSecret();
|
//微信公众号SecretKey
|
private static final String wxPublicSecretKey = payProperties.getWxPublicSecretKey();
|
//微信支付密钥
|
private static final String wxPayKey = payProperties.getWxPayKey();
|
// 微信证书位置
|
private static String wxCertPath = payProperties.getWxCertPath();
|
|
static {
|
if (!wxCertPath.contains("/tmp/")) {
|
wxCertPath = ClassUtils.getDefaultClassLoader().getResource("").getPath() + wxCertPath;
|
}
|
}
|
|
//微信支付查询地址
|
private static final String wxCheckUrl = "https://api.mch.weixin.qq.com/pay/orderquery";
|
//微信AccessToken临时缓存
|
private static String wxAccessToken = "";
|
|
static {
|
alipayClient = new DefaultAlipayClient(
|
"https://openapi.alipay.com/gateway.do",
|
ALI_APP_ID,
|
PRIVATE_KEY,
|
"json",
|
"UTF-8",
|
ALI_PUBLIC_KEY,
|
"RSA2");
|
}
|
|
private static AliPayApiConfig aliPayApiConfig = null;
|
|
static {
|
aliPayApiConfig = AliPayApiConfig.New()
|
.setAppId(ALI_APP_ID)
|
.setCharset("UTF-8")
|
.setPrivateKey(PRIVATE_KEY)
|
.setAlipayPublicKey(ALI_PUBLIC_KEY)
|
.setServiceUrl("https://openapi.alipay.com/gateway.do")
|
.setSignType("RSA2")
|
.build();
|
}
|
|
/**
|
* 微信app支付
|
*
|
* @param wxPay
|
* @param request
|
*/
|
public static Map<String, String> wxAppPay(WxPay wxPay, HttpServletRequest request) {
|
//生成32位随机数
|
UUID uuid = UUID.randomUUID();
|
String nonceStr = uuid.toString().replaceAll("-", "");
|
//商品描述 String body = "XX商城-支付订单";
|
// 创建hashmap(用户获得签名)
|
SortedMap<String, String> paraMap = new TreeMap<>();
|
//设置请求参数(小程序ID)
|
paraMap.put("appid", wxPay.getAppId());
|
//设置请求参数(商户号)
|
paraMap.put("mch_id", wxPay.getMchId());
|
//设置请求参数(随机字符串)
|
paraMap.put("nonce_str", nonceStr);
|
//设置请求参数(商品描述)
|
paraMap.put("body", wxPay.getBody());
|
//设置请求参数(商户订单号)
|
paraMap.put("out_trade_no", wxPay.getOutTradeNo());
|
//设置请求参数(总金额)
|
paraMap.put("total_fee", wxPay.getTotalFee());
|
//设置请求参数(终端IP) 如果是springmvc,或者能获取到request的servlet,用下面这种
|
paraMap.put("spbill_create_ip", getIpAddress(request));
|
//设置请求参数(通知地址)
|
paraMap.put("notify_url", wxPay.getNotifyUrl());
|
//设置请求参数(交易类型)
|
paraMap.put("trade_type", String.valueOf(WxPayApi.TradeType.APP));
|
//设置请求参数(openid)(在接口文档中 该参数 是否必填项 但是一定要注意 如果交易类型设置成'JSAPI'则必须传入openid)
|
//MD5运算生成签名,这里是第一次签名,用于调用统一下单接口
|
String sign = PaymentKit.createSign(paraMap, wxPay.getKey());
|
paraMap.put("sign", sign);
|
//统一下单,向微信api发送数据
|
log.info("微信APP统一下单发送的数据: " + paraMap.toString());
|
String xmlResult = WxPayApi.pushOrder(false, paraMap);
|
log.info("微信APP统一下单接受返回的结果: " + xmlResult);
|
//转成xml
|
Map<String, String> map = PaymentKit.xmlToMap(xmlResult);
|
//返回状态码
|
String returnCode = map.get("return_code");
|
Assert.isTrue("SUCCESS".equals(returnCode), getMsgByCode(returnCode));
|
|
//返回给小程序端需要的参数
|
Map<String, String> returnMap = new HashMap<>(20);
|
//返回的预付单信息
|
returnMap.put("appid", wxPay.getAppId());
|
returnMap.put("noncestr", nonceStr);
|
returnMap.put("package", "Sign=WXPay");
|
returnMap.put("partnerid", wxPay.getMchId());
|
returnMap.put("prepayid", map.get("prepay_id"));
|
//这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误
|
returnMap.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
|
//拼接签名需要的参数
|
// 再次签名,这个签名用于小程序端调用wx.requesetPayment方法
|
String paySign = PaymentKit.createSign(returnMap, wxPay.getKey()).toUpperCase();
|
returnMap.put("paySign", paySign);
|
returnMap.put("prepay_id", map.get("prepay_id"));
|
return returnMap;
|
}
|
|
/**
|
* 微信app支付
|
*
|
* @param wxPay
|
* @param request
|
*/
|
public static Map<String, Object> wxJsApiPay(WxPay wxPay, HttpServletRequest request) {
|
//生成32位随机数
|
UUID uuid = UUID.randomUUID();
|
String nonceStr = uuid.toString().replaceAll("-", "");
|
//商品描述 String body = "XX商城-支付订单";
|
// 创建hashmap(用户获得签名)
|
SortedMap<String, String> paraMap = new TreeMap<>();
|
//设置请求参数(小程序ID)
|
paraMap.put("appid", wxPay.getAppId());
|
//设置请求参数(商户号)
|
paraMap.put("mch_id", wxPay.getMchId());
|
//设置请求参数(随机字符串)
|
paraMap.put("nonce_str", nonceStr);
|
//设置请求参数(商品描述)
|
paraMap.put("body", wxPay.getBody());
|
//设置请求参数(商户订单号)
|
paraMap.put("out_trade_no", wxPay.getOutTradeNo());
|
//设置请求参数(总金额)
|
paraMap.put("total_fee", wxPay.getTotalFee());
|
//设置请求参数(终端IP) 如果是springmvc,或者能获取到request的servlet,用下面这种
|
paraMap.put("spbill_create_ip", getIpAddress(request));
|
//设置请求参数(通知地址)
|
paraMap.put("notify_url", wxPay.getNotifyUrl());
|
//设置请求参数(交易类型)
|
paraMap.put("trade_type", String.valueOf(WxPayApi.TradeType.JSAPI));
|
if (wxPay.getOpenId() != null) {
|
paraMap.put("openid", wxPay.getOpenId());
|
}
|
//设置请求参数(openid)(在接口文档中 该参数 是否必填项 但是一定要注意 如果交易类型设置成'JSAPI'则必须传入openid)
|
//MD5运算生成签名,这里是第一次签名,用于调用统一下单接口
|
String sign = PaymentKit.createSign(paraMap, wxPay.getKey());
|
paraMap.put("sign", sign);
|
//统一下单,向微信api发送数据
|
log.info("微信支付统一下单发送的数据: " + paraMap.toString());
|
String xmlResult = WxPayApi.pushOrder(false, paraMap);
|
log.info("微信支付统一下单接受返回的结果: " + xmlResult);
|
//转成xml
|
Map<String, String> map = PaymentKit.xmlToMap(xmlResult);
|
//返回状态码
|
String returnCode = map.get("return_code");
|
Assert.isTrue("SUCCESS".equals(returnCode), getMsgByCode(returnCode));
|
|
//返回给小程序端需要的参数
|
Map<String, Object> returnMap = new HashMap<>(20);
|
|
String prepay_id = map.get("prepay_id");
|
//重新进行签名后返回给前端
|
returnMap.put("appId", map.get("appid"));
|
returnMap.put("nonceStr", map.get("nonce_str"));
|
returnMap.put("package", "prepay_id=" + prepay_id);
|
returnMap.put("timeStamp", new Date().getTime() + "");
|
returnMap.put("signType", "MD5");
|
String signature = weixinSignature(returnMap, wxPay.getKey());
|
|
returnMap.put("prepay_id", prepay_id);
|
returnMap.put("mch_id", map.get("mch_id"));
|
returnMap.put("trade_type", map.get("trade_type"));
|
|
returnMap.put("sign", signature);
|
returnMap.put("err_code_des", map.get("err_code_des"));
|
|
return returnMap;
|
}
|
|
/**
|
* 微信下单的签名算法
|
*
|
* @param map
|
* @return
|
*/
|
private static String weixinSignature(Map<String, Object> map, String privateKey) {
|
try {
|
Set<Map.Entry<String, Object>> entries = map.entrySet();
|
List<Map.Entry<String, Object>> infoIds = new ArrayList<Map.Entry<String, Object>>(entries);
|
// 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
|
Collections.sort(infoIds, new Comparator<Map.Entry<String, Object>>() {
|
public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {
|
return (o1.getKey()).toString().compareTo(o2.getKey());
|
}
|
});
|
// 构造签名键值对的格式
|
StringBuilder sb = new StringBuilder();
|
for (Map.Entry<String, Object> item : infoIds) {
|
if (item.getKey() != null || item.getKey() != "") {
|
String key = item.getKey();
|
Object val = item.getValue();
|
if (!(val == "" || val == null)) {
|
sb.append(key + "=" + val + "&");
|
}
|
}
|
}
|
sb.append("key=" + privateKey);
|
String sign = MD5AndKL.MD5Encode(sb.toString(), "UTF-8").toUpperCase(); //注:MD5签名方式
|
return sign;
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
return null;
|
}
|
|
|
/**
|
* 支付宝支付
|
*
|
* @param orderNo
|
* @param totalAmount
|
* @param notifyUrl
|
*/
|
public static String AliPayAppPay(String orderNo, String totalAmount, String notifyUrl) {
|
AliPayApiConfigKit.setThreadLocalAppId(ALI_APP_ID);
|
AliPayApiConfigKit.setThreadLocalAliPayApiConfig(aliPayApiConfig);
|
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
|
model.setBody("美天美牙支付");
|
model.setSubject("美天美牙订单");
|
model.setOutTradeNo(orderNo);
|
model.setTimeoutExpress("30m");
|
model.setTotalAmount(totalAmount);
|
model.setPassbackParams("callback params");
|
model.setProductCode("QUICK_MSECURITY_PAY");
|
String orderInfo = "";
|
try {
|
orderInfo = AliPayApi.startAppPay(model, notifyUrl);
|
} catch (AlipayApiException e) {
|
e.printStackTrace();
|
}
|
log.debug("支付宝支付返回:{}", orderInfo);
|
return orderInfo;
|
}
|
|
public static String AliPayAppPay2(String orderNo, String totalAmount, String notifyUrl, String body, String subject) {
|
AliPayApiConfigKit.setThreadLocalAppId(ALI_APP_ID);
|
AliPayApiConfigKit.setThreadLocalAliPayApiConfig(aliPayApiConfig);
|
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
|
model.setBody(body);
|
model.setSubject(subject);
|
model.setOutTradeNo(orderNo);
|
model.setTimeoutExpress("30m");
|
model.setTotalAmount(totalAmount);
|
model.setPassbackParams("callback params");
|
model.setProductCode("QUICK_MSECURITY_PAY");
|
String orderInfo = "";
|
try {
|
orderInfo = AliPayApi.startAppPay(model, notifyUrl);
|
} catch (AlipayApiException e) {
|
e.printStackTrace();
|
}
|
log.debug("支付宝支付返回:{}", orderInfo);
|
return orderInfo;
|
}
|
|
/**
|
* 企业付款到零钱
|
*/
|
public static boolean wxTransfers(WxPay wxPay) {
|
Map<String, String> params = new HashMap<String, String>();
|
params.put("mch_appid", wxPay.getAppId());
|
params.put("mchid", wxPay.getMchId());
|
String nonceStr = String.valueOf(System.currentTimeMillis());
|
params.put("nonce_str", nonceStr);
|
String partnerTradeNo = String.valueOf(System.currentTimeMillis());
|
params.put("partner_trade_no", partnerTradeNo);
|
params.put("openid", wxPay.getOpenId());
|
params.put("check_name", "NO_CHECK");
|
params.put("amount", "100");
|
params.put("desc", "IJPay提现测试-By Javen");
|
String ip = "127.0.0.1";
|
params.put("spbill_create_ip", ip);
|
|
params.put("sign", PaymentKit.createSign(params, wxPay.getKey()));
|
String certPath = "E:\\workspace\\caihui-parent\\guns-rest\\src\\main\\resources\\cert\\apiclient_cert.p12";
|
System.out.println("certPath:" + certPath);
|
//sysConfig.getUploadPath() + "cert/apiclient_cert.p12");
|
// 提现
|
String transfers = WxPayApi.transfers(params, certPath, wxPay.getMchId());
|
|
log.info("提现结果:" + transfers);
|
|
Map<String, String> map = PaymentKit.xmlToMap(transfers);
|
String return_code = map.get("return_code");
|
String result_code = null;
|
boolean transfer = false;
|
if (("SUCCESS").equals(return_code)) {
|
result_code = map.get("result_code");
|
if (("SUCCESS").equals(result_code)) {
|
//提现成功
|
transfer = true;
|
} else {
|
//提现失败
|
}
|
}
|
return transfer;
|
}
|
|
|
/**
|
* 支付宝转账
|
*
|
* @param orderNo
|
* @param totalAmount
|
* @return
|
*/
|
public static boolean AliPayTransfer(String orderNo, String totalAmount, String aliAccount) {
|
AliPayApiConfigKit.setThreadLocalAppId(ALI_APP_ID);
|
AliPayApiConfigKit.setThreadLocalAliPayApiConfig(aliPayApiConfig);
|
AlipayFundTransToaccountTransferModel model = new AlipayFundTransToaccountTransferModel();
|
model.setOutBizNo(orderNo);
|
model.setPayeeType("ALIPAY_LOGONID");
|
model.setPayeeAccount("837247258@qq.com");
|
model.setAmount(totalAmount);
|
model.setPayerShowName("测试");
|
model.setPayerRealName("测试");
|
model.setPayeeRealName("唐宗君");
|
//model.setExtParam("DFJK");
|
model.setRemark("java测试单笔转账到支付宝");
|
boolean transfer = false;
|
try {
|
transfer = AliPayApi.transfer(model);
|
} catch (AlipayApiException e) {
|
e.printStackTrace();
|
}
|
return transfer;
|
}
|
|
public static PayResult startWebAlipay(String orderNo, String orderName, BigDecimal price, boolean debug) {
|
PayResult r = new PayResult();
|
AlipayTradeWapPayRequest aliRequest = new AlipayTradeWapPayRequest();
|
aliRequest.setReturnUrl(aliReturnUrl);
|
aliRequest.setNotifyUrl(aliNotifyUrl);
|
AlipayTradeWapPayModel mode = new AlipayTradeWapPayModel();
|
mode.setOutTradeNo(orderNo);
|
mode.setSubject(orderName);
|
if (debug) {
|
mode.setTotalAmount("0.01");
|
} else {
|
mode.setTotalAmount(price.toString());
|
}
|
mode.setBody("实名认证费用");
|
mode.setProductCode("QUICK_WAP_WAY");
|
mode.setSellerId(aliId);
|
aliRequest.setBizModel(mode);
|
String from = "";
|
try {
|
from = alipayClient.pageExecute(aliRequest, "get").getBody();
|
r.success = true;
|
r.url = from;
|
} catch (AlipayApiException e) {
|
r.success = false;
|
r.msg = e.getErrMsg();
|
}
|
return r;
|
}
|
|
public static PayResult startWebWechatPay(HttpServletRequest request, String orderNo, String orderName, BigDecimal price, boolean debug) {
|
// 获取预支付接口返回参数
|
// 测试
|
if (debug) {
|
price = BigDecimal.valueOf(0.01);
|
}
|
Map<String, Object> map;
|
PayResult r = new PayResult();
|
try {
|
WxPay payInfo = new WxPay();
|
payInfo.setAppId(wxServiceAppId);
|
payInfo.setMchId(wxMchId);
|
payInfo.setBody(orderName);
|
payInfo.setOutTradeNo(orderNo);
|
payInfo.setTotalFee(String.valueOf(price.multiply(BigDecimal.valueOf(100)).intValue()));
|
payInfo.setNotifyUrl(notifyUrl);
|
payInfo.setKey(secretKey);
|
String url = wxH5Pay(payInfo, request);
|
r.success = !url.isEmpty();
|
r.url = url;
|
} catch (Exception e) {
|
r.success = false;
|
r.msg = e.getMessage();
|
e.printStackTrace();
|
}
|
return r;
|
}
|
|
public static PayResult startWechatPublicPay(HttpServletRequest request, String orderNo, String orderName, String openId, BigDecimal price, boolean debug) {
|
// 获取预支付接口返回参数
|
// 测试
|
if (debug) {
|
price = BigDecimal.valueOf(0.01);
|
}
|
Map<String, Object> map;
|
PayResult r = new PayResult();
|
try {
|
WxPay payInfo = new WxPay();
|
payInfo.setAppId(wxServiceAppId);
|
payInfo.setMchId(wxMchId);
|
payInfo.setBody(orderName);
|
payInfo.setOutTradeNo(orderNo);
|
payInfo.setTotalFee(String.valueOf(price.multiply(BigDecimal.valueOf(100)).intValue()));
|
payInfo.setNotifyUrl(notifyUrl);
|
payInfo.setKey(secretKey);
|
payInfo.setOpenId(openId);
|
WxSignVO vo = wxPublicPay(payInfo, request);
|
r.success = true;
|
r.setVo(vo);
|
} catch (Exception e) {
|
r.success = false;
|
r.msg = e.getMessage();
|
e.printStackTrace();
|
}
|
return r;
|
}
|
|
public static AlipayTradeQueryResponse checkAlipayResult(String orderNo, String orderName, BigDecimal price, boolean debug) {
|
AlipayTradeQueryModel query = new AlipayTradeQueryModel();
|
query.setOutTradeNo(orderNo);
|
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
|
request.setApiVersion("1.0");
|
request.setBizModel(query);
|
try {
|
AlipayTradeQueryResponse r = alipayClient.execute(request);
|
return r;
|
} catch (Exception e) {
|
return null;
|
}
|
}
|
|
public static WxPayResult checkWxPayResult(String orderNo, String orderName, BigDecimal price, boolean debug) {
|
Map<String, String> params = new HashMap<>();
|
params.put("appid", wxServiceAppId);
|
params.put("mch_id", wxMchId);
|
params.put("out_trade_no", orderNo);
|
//生成32位随机数
|
UUID uuid = UUID.randomUUID();
|
String nonceStr = uuid.toString().replaceAll("-", "");
|
params.put("nonce_str", nonceStr);
|
//MD5运算生成签名
|
String sign = PaymentKit.createSign(params, secretKey);
|
params.put("sign", sign);
|
String xmlString = PaymentKit.toXml(params);
|
String xmlResult = WxPayApi.orderQuery(false, params);
|
//结果xml转为map
|
Map<String, String> map = PaymentKit.xmlToMap(xmlResult);
|
//返回状态码
|
String returnCode = map.get("return_code");
|
Assert.isTrue("SUCCESS".equals(returnCode), getMsgByCode(returnCode));
|
WxPayResult result = new WxPayResult();
|
//返回业务状态码
|
String resultCode = map.get("result_code");
|
//返回交易状态
|
String resultState = map.get("trade_state");
|
//返回的交易号
|
String transactionId = map.get("transaction_id");
|
if ("SUCCESS".equals(resultCode) && "SUCCESS".equals(resultState) && !StringUtils.isEmpty(transactionId)) {
|
result.setSuccess(true);
|
result.setTransactionId(transactionId);
|
} else {
|
result.setSuccess(false);
|
}
|
return result;
|
}
|
|
public static Map<String, String> wrapAliPayRequest(HttpServletRequest request) {
|
Map<String, String> retMap = new HashMap<String, String>();
|
|
Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();
|
|
for (Map.Entry<String, String[]> entry : entrySet) {
|
String name = entry.getKey();
|
String[] values = entry.getValue();
|
int valLen = values.length;
|
|
if (valLen == 1) {
|
retMap.put(name, values[0]);
|
} else if (valLen > 1) {
|
StringBuilder sb = new StringBuilder();
|
for (String val : values) {
|
sb.append(",").append(val);
|
}
|
retMap.put(name, sb.toString().substring(1));
|
} else {
|
retMap.put(name, "");
|
}
|
}
|
|
return retMap;
|
}
|
|
public static boolean alipayVerifyV1(Map<String, String> params) throws Exception {
|
return AlipaySignature.rsaCheckV1(params, ALI_PUBLIC_KEY,
|
"UTF-8", "RSA2");
|
}
|
|
public static boolean alipayVerify(Map<String, String> params) throws Exception {
|
return AlipaySignature.rsaCheckV2(params, ALI_PUBLIC_KEY,
|
"UTF-8", "RSA2");
|
}
|
|
/**
|
* 微信H5支付
|
*
|
* @param wxPay
|
* @param request
|
*/
|
public static String wxH5Pay(WxPay wxPay, HttpServletRequest request) {
|
//生成32位随机数
|
UUID uuid = UUID.randomUUID();
|
String nonceStr = uuid.toString().replaceAll("-", "");
|
//商品描述 String body = "XX商城-支付订单";
|
// 创建hashmap(用户获得签名)
|
SortedMap<String, String> paraMap = new TreeMap<>();
|
//设置请求参数(小程序ID)
|
paraMap.put("appid", wxPay.getAppId());
|
//设置请求参数(商户号)
|
paraMap.put("mch_id", wxPay.getMchId());
|
//设置请求参数(随机字符串)
|
paraMap.put("nonce_str", nonceStr);
|
//设置请求参数(商品描述)
|
paraMap.put("body", wxPay.getBody());
|
//设置请求参数(商户订单号)
|
paraMap.put("out_trade_no", wxPay.getOutTradeNo());
|
//设置请求参数(总金额)
|
paraMap.put("total_fee", wxPay.getTotalFee());
|
//设置请求参数(终端IP) 如果是springmvc,或者能获取到request的servlet,用下面这种
|
paraMap.put("spbill_create_ip", getIpAddress(request));
|
//设置请求参数(通知地址)
|
paraMap.put("notify_url", wxPay.getNotifyUrl());
|
//设置请求参数(交易类型)
|
paraMap.put("trade_type", String.valueOf(WxPayApi.TradeType.MWEB));
|
//重定向地址
|
// paraMap.put("redirect_url", wxReturnUrl);
|
//设置请求参数(openid)(在接口文档中 该参数 是否必填项 但是一定要注意 如果交易类型设置成'JSAPI'则必须传入openid)
|
//MD5运算生成签名,这里是第一次签名,用于调用统一下单接口
|
String sign = PaymentKit.createSign(paraMap, wxPay.getKey());
|
paraMap.put("sign", sign);
|
//统一下单,向微信api发送数据
|
log.debug("微信下单发送的数据: " + paraMap.toString());
|
String xmlResult = WxPayApi.pushOrder(false, paraMap);
|
log.debug("微信下单接受返回的结果: " + xmlResult);
|
//转成xml
|
Map<String, String> map = PaymentKit.xmlToMap(xmlResult);
|
//返回状态码
|
String returnCode = map.get("return_code");
|
Assert.isTrue("SUCCESS".equals(returnCode), getMsgByCode(returnCode));
|
return map.get("mweb_url");
|
}
|
|
/**
|
* 微信公众号支付
|
*
|
* @param wxPay
|
* @param request
|
*/
|
public static WxSignVO wxPublicPay(WxPay wxPay, HttpServletRequest request) {
|
//生成32位随机数
|
UUID uuid = UUID.randomUUID();
|
String nonceStr = uuid.toString().replaceAll("-", "");
|
//商品描述 String body = "XX商城-支付订单";
|
// 创建hashmap(用户获得签名)
|
SortedMap<String, String> paraMap = new TreeMap<>();
|
//设置请求参数(小程序ID)
|
paraMap.put("appid", wxPay.getAppId());
|
//设置请求参数(商户号)
|
paraMap.put("mch_id", wxPay.getMchId());
|
//设置请求参数(随机字符串)
|
paraMap.put("nonce_str", nonceStr);
|
//设置请求参数(商品描述)
|
paraMap.put("body", wxPay.getBody());
|
//设置请求参数(商户订单号)
|
paraMap.put("out_trade_no", wxPay.getOutTradeNo());
|
//设置请求参数(总金额)
|
paraMap.put("total_fee", wxPay.getTotalFee());
|
//设置请求参数(终端IP) 如果是springmvc,或者能获取到request的servlet,用下面这种
|
//设置请求参数(openid)(在接口文档中 该参数 是否必填项 但是一定要注意 如果交易类型设置成'JSAPI'则必须传入openid)
|
//MD5运算生成签名,这里是第一次签名,用于调用统一下单接口
|
paraMap.put("spbill_create_ip", getIpAddress(request));
|
//设置请求参数(通知地址)
|
paraMap.put("notify_url", wxPay.getNotifyUrl());
|
//设置请求参数(交易类型)
|
paraMap.put("trade_type", String.valueOf(WxPayApi.TradeType.JSAPI));
|
//设置openId
|
paraMap.put("openid", wxPay.getOpenId());
|
//MD5运算生成签名,这里是第一次签名,用于调用统一下单接口
|
String sign = PaymentKit.createSign(paraMap, wxPay.getKey());
|
paraMap.put("sign", sign);
|
//统一下单,向微信api发送数据
|
log.debug("微信下单发送的数据: " + paraMap.toString());
|
String xmlResult = WxPayApi.pushOrder(false, paraMap);
|
log.debug("微信下单接受返回的结果: " + xmlResult);
|
//转成xml
|
Map<String, String> map = PaymentKit.xmlToMap(xmlResult);
|
//返回状态码
|
String returnCode = map.get("return_code");
|
Assert.isTrue("SUCCESS".equals(returnCode), getMsgByCode(returnCode));
|
Map<String, String> signMap = new HashMap<>();
|
signMap.put("appId", wxServiceAppId);
|
signMap.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
|
signMap.put("nonceStr", nonceStr);
|
signMap.put("package", "prepay_id=" + map.get("prepay_id"));
|
signMap.put("signType", "MD5");
|
String paySign = PaymentKit.createSign(signMap, wxPay.getKey()).toUpperCase();
|
WxSignVO vo = new WxSignVO();
|
vo.setAppId(wxServiceAppId);
|
vo.setTimeStamp(signMap.get("timeStamp"));
|
vo.setNonceStr(nonceStr);
|
vo.setPrepayId(signMap.get("package"));
|
vo.setPaySign(paySign);
|
return vo;
|
}
|
|
public static String getWxOpenId(String code) {
|
String openId = "";
|
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + wxServiceAppId + "&secret=" + wxPublicSecretKey + "&code=" + code + "&grant_type=authorization_code";
|
try {
|
URL urlGet = new URL(url);
|
HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
|
// 必须是get方式请求
|
http.setRequestMethod("GET");
|
http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
http.setDoOutput(true);
|
http.setDoInput(true);
|
// 连接超时30秒
|
System.setProperty("sun.net.client.defaultConnectTimeout", "30000");
|
// 读取超时30秒
|
System.setProperty("sun.net.client.defaultReadTimeout", "30000");
|
http.connect();
|
InputStream is = http.getInputStream();
|
int size = is.available();
|
byte[] jsonBytes = new byte[size];
|
is.read(jsonBytes);
|
String message = new String(jsonBytes, "UTF-8");
|
if (message != null && message.contains("errcode")) {
|
// 错误的直接返回
|
return null;
|
}
|
JSONObject demoJson = JSONObject.fromObject(message);
|
|
openId = demoJson.getString("openid");
|
is.close();
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
return openId;
|
}
|
|
public static String getWebAccessToken(String code) {
|
String access_token = "";
|
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + wxServiceAppId + "&secret=" + secretKey + "&code=" + code + "&grant_type=authorization_code";
|
try {
|
URL urlGet = new URL(url);
|
HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
|
// 必须是get方式请求
|
http.setRequestMethod("GET");
|
http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
http.setDoOutput(true);
|
http.setDoInput(true);
|
// 连接超时30秒
|
System.setProperty("sun.net.client.defaultConnectTimeout", "30000");
|
// 读取超时30秒
|
System.setProperty("sun.net.client.defaultReadTimeout", "30000");
|
http.connect();
|
InputStream is = http.getInputStream();
|
int size = is.available();
|
byte[] jsonBytes = new byte[size];
|
is.read(jsonBytes);
|
String message = new String(jsonBytes, "UTF-8");
|
JSONObject demoJson = JSONObject.fromObject(message);
|
System.out.println("JSON字符串:" + demoJson);
|
access_token = demoJson.getString("access_token");
|
is.close();
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
return access_token;
|
}
|
|
public static String getAccessToken() {
|
String access_token = "";
|
//获取access_token填写client_credential
|
String grant_type = "client_credential";
|
//这个url链接地址和参数皆不能变
|
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=" + grant_type + "&appid=" + wxServiceAppId + "&secret=" + wxPublicSecretKey;
|
try {
|
URL urlGet = new URL(url);
|
HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
|
// 必须是get方式请求
|
http.setRequestMethod("GET");
|
http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
http.setDoOutput(true);
|
http.setDoInput(true);
|
// 连接超时30秒
|
System.setProperty("sun.net.client.defaultConnectTimeout", "30000");
|
// 读取超时30秒
|
System.setProperty("sun.net.client.defaultReadTimeout", "30000");
|
http.connect();
|
InputStream is = http.getInputStream();
|
int size = is.available();
|
byte[] jsonBytes = new byte[size];
|
is.read(jsonBytes);
|
String message = new String(jsonBytes, "UTF-8");
|
JSONObject demoJson = JSONObject.fromObject(message);
|
System.out.println("JSON字符串:" + demoJson);
|
access_token = demoJson.getString("access_token");
|
is.close();
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
wxAccessToken = access_token;
|
return access_token;
|
}
|
|
public static String getTicket(String access_token) {
|
String ticket = null;
|
//这个url链接和参数不能变
|
String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + access_token + "&type=jsapi";
|
try {
|
URL urlGet = new URL(url);
|
HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
|
// 必须是get方式请求
|
http.setRequestMethod("GET");
|
http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
http.setDoOutput(true);
|
http.setDoInput(true);
|
// 连接超时30秒
|
System.setProperty("sun.net.client.defaultConnectTimeout", "30000");
|
// 读取超时30秒
|
System.setProperty("sun.net.client.defaultReadTimeout", "30000");
|
http.connect();
|
InputStream is = http.getInputStream();
|
int size = is.available();
|
byte[] jsonBytes = new byte[size];
|
is.read(jsonBytes);
|
String message = new String(jsonBytes, "UTF-8");
|
JSONObject demoJson = JSONObject.fromObject(message);
|
System.out.println("JSON字符串:" + demoJson);
|
ticket = demoJson.getString("ticket");
|
is.close();
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
return ticket;
|
}
|
|
/**
|
* <p>
|
* 获取JSSDK签名
|
* </p>
|
*
|
* @param url 当前页面的完整URL,包括参数
|
* @param jsapi_ticket
|
* @return java.util.Map
|
* @author BaiHua
|
* @Description
|
* @date 2019/12/13 18:09
|
*/
|
public static Map getJsApiConfig(String url, String jsapi_ticket) {
|
String noncestr = UUID.randomUUID().toString().replace("-", "");
|
String timestamp = "" + System.currentTimeMillis() / 1000;
|
Map<String, String> params = new HashMap<>(4);
|
params.put("jsapi_ticket", jsapi_ticket);
|
params.put("noncestr", noncestr);
|
params.put("timestamp", timestamp);
|
params.put("url", url);
|
//1.1 对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)
|
Map<String, String> sortParams = sortAsc(params);
|
//1.2 使用URL键值对的格式拼接成字符串
|
String str = mapJoin(sortParams, false);
|
String signature = SHA1(str);
|
Map<String, String> result = new HashMap<>(5);
|
result.put("debug", "false");
|
result.put("appId", wxServiceAppId);
|
result.put("nonceStr", noncestr);
|
result.put("timestamp", timestamp);
|
result.put("signature", signature);
|
return result;
|
}
|
|
public static String mapJoin(Map<String, String> map, boolean valueUrlEncode) {
|
StringBuilder sb = new StringBuilder();
|
for (String key : map.keySet()) {
|
if (map.get(key) != null && !"".equals(map.get(key))) {
|
try {
|
String temp = (key.endsWith("_") && key.length() > 1) ? key.substring(0, key.length() - 1) : key;
|
sb.append(temp);
|
sb.append("=");
|
//获取到map的值
|
String value = map.get(key);
|
//判断是否需要url编码
|
if (valueUrlEncode) {
|
value = URLEncoder.encode(map.get(key), "utf-8").replace("+", "%20");
|
}
|
sb.append(value);
|
sb.append("&");
|
} catch (UnsupportedEncodingException e) {
|
e.printStackTrace();
|
}
|
}
|
}
|
if (sb.length() > 0) {
|
sb.deleteCharAt(sb.length() - 1);
|
}
|
return sb.toString();
|
}
|
|
|
public static String SHA1(String decript) {
|
try {
|
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
digest.update(decript.getBytes());
|
byte messageDigest[] = digest.digest();
|
// Create Hex String
|
StringBuffer hexString = new StringBuffer();
|
// 字节数组转换为 十六进制 数
|
for (int i = 0; i < messageDigest.length; i++) {
|
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
|
if (shaHex.length() < 2) {
|
hexString.append(0);
|
}
|
hexString.append(shaHex);
|
}
|
return hexString.toString();
|
|
} catch (NoSuchAlgorithmException e) {
|
e.printStackTrace();
|
}
|
return "";
|
}
|
|
private static HashMap<String, String> sortAsc(Map<String, String> map) {
|
HashMap<String, String> tempMap = new LinkedHashMap<String, String>();
|
List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(map.entrySet());
|
//排序
|
Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {
|
@Override
|
public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
|
return o1.getKey().compareTo(o2.getKey());
|
}
|
});
|
for (int i = 0; i < infoIds.size(); i++) {
|
Map.Entry<String, String> item = infoIds.get(i);
|
tempMap.put(item.getKey(), item.getValue());
|
}
|
return tempMap;
|
}
|
|
/**
|
* @param @param request
|
* @param @param response
|
* @param @return 参数
|
* @return String 返回类型
|
* @throws
|
* @Title: getIpAddress
|
* @Description: 获取客户端真实IP地址
|
* @author yihj
|
*/
|
private static String getIpAddress(HttpServletRequest request) {
|
// 避免反向代理不能获取真实地址, 取X-Forwarded-For中第一个非unknown的有效IP字符串
|
String ip = request.getHeader("x-forwarded-for");
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
ip = request.getHeader("Proxy-Client-IP");
|
}
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
ip = request.getHeader("WL-Proxy-Client-IP");
|
}
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
ip = request.getRemoteAddr();
|
}
|
return ip;
|
}
|
|
/**
|
* 微信退款
|
*/
|
public static Map<String, String> wxRefund(String orderNo, String outTradeNo, BigDecimal payTotalFee, BigDecimal refundAmount) {
|
WxPay wxPay = new WxPay(wxAppId, wxMchId, orderNo,
|
payTotalFee.intValue() + "", outTradeNo, new BigDecimal(100).multiply(refundAmount).intValue() + "",
|
"TK" + orderNo + ToolUtil.getRandomNumber(4), wxPayKey);
|
|
//生成32位随机数
|
UUID uuid = UUID.randomUUID();
|
String nonceStr = uuid.toString().replaceAll("-", "");
|
//商品描述 String body = "XX商城-支付订单";
|
// 创建hashmap(用户获得签名)
|
SortedMap<String, String> paraMap = new TreeMap<>();
|
//设置请求参数(小程序ID)
|
paraMap.put("appid", wxPay.getAppId());
|
//设置请求参数(商户号)
|
paraMap.put("mch_id", wxPay.getMchId());
|
//设置请求参数(随机字符串)
|
paraMap.put("nonce_str", nonceStr);
|
paraMap.put("transaction_id", wxPay.getTransactionId());
|
paraMap.put("out_trade_no", wxPay.getOutTradeNo());
|
paraMap.put("out_refund_no", wxPay.getOutRefundNo());
|
//设置请求参数(支付的总金额)
|
paraMap.put("total_fee", wxPay.getTotalFee());
|
//设置请求参数(退款的金额)
|
paraMap.put("refund_fee", wxPay.getRefundFee());
|
//MD5运算生成签名
|
paraMap.put("sign", PaymentKit.createSign(paraMap, wxPay.getKey()));
|
//统一下单,向微信api发送数据
|
log.info("证书: " + wxCertPath);
|
log.info("微信App退款申请发送的数据: " + paraMap.toString());
|
String xmlResult = WxPayApi.orderRefund(false, paraMap, wxCertPath, wxPay.getMchId());
|
log.info("微信App退款申请返回的结果: " + xmlResult);
|
return PaymentKit.xmlToMap(xmlResult);
|
}
|
|
/**
|
* 支付宝退款
|
*
|
* @param orderNo 退款订单
|
* @param tradeNo 交易号
|
* @param refundAmount 退款金额
|
* @return
|
*/
|
public static boolean aliRefund(String orderNo, String tradeNo, String refundAmount) {
|
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", ALI_APP_ID, PRIVATE_KEY, "json", "GBK", ALI_PUBLIC_KEY, "RSA2");
|
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
|
request.setBizContent("{" +
|
"\"out_trade_no\":\"" + orderNo + "\"," +
|
"\"trade_no\":\"" + tradeNo + "\"," +
|
"\"refund_amount\":" + refundAmount + "," +
|
"\"refund_currency\":\"CNY\"," +
|
"\"refund_reason\":\"正常退款\"" +
|
// "\"out_request_no\":\"HZ01RF001\"," +
|
// "\"operator_id\":\"OP001\"," +
|
// "\"store_id\":\"NJ_S_001\"," +
|
// "\"terminal_id\":\"NJ_T_001\"," +
|
// " \"goods_detail\":[{" +
|
// " \"goods_id\":\"apple-01\"," +
|
// "\"alipay_goods_id\":\"20010001\"," +
|
// "\"goods_name\":\"ipad\"," +
|
// "\"quantity\":1," +
|
// "\"price\":2000," +
|
// "\"goods_category\":\"34543238\"," +
|
// "\"categories_tree\":\"124868003|126232002|126252004\"," +
|
// "\"body\":\"特价手机\"," +
|
// "\"show_url\":\"http://www.alipay.com/xxx.jpg\"" +
|
// " }]," +
|
// " \"refund_royalty_parameters\":[{" +
|
// " \"royalty_type\":\"transfer\"," +
|
// "\"trans_out\":\"2088101126765726\"," +
|
// "\"trans_out_type\":\"userId\"," +
|
// "\"trans_in_type\":\"userId\"," +
|
// "\"trans_in\":\"2088101126708402\"," +
|
// "\"amount\":0.1," +
|
// "\"amount_percentage\":100," +
|
// "\"desc\":\"分账给2088101126708402\"" +
|
// " }]," +
|
// "\"org_pid\":\"2088101117952222\"" +
|
" }");
|
AlipayTradeRefundResponse response = null;
|
try {
|
response = alipayClient.execute(request);
|
} catch (AlipayApiException e) {
|
e.printStackTrace();
|
}
|
if (response.isSuccess()) {
|
log.info("支付宝退款调用成功:{},{}", response.getSubMsg(), response.getBody());
|
return true;
|
} else {
|
log.error("支付宝退款调用失败:{},{},{}", response.getSubMsg(), response.getBody(), response.getSubCode());
|
}
|
return false;
|
}
|
|
private static String getMsgByCode(String returnCode) {
|
switch (returnCode) {
|
case "NOTENOUGH":
|
return "您的账户余额不足";
|
case "ORDERPAID":
|
return "该订单已支付完成,请勿重复支付";
|
case "ORDERCLOSED":
|
return "当前订单已关闭,请重新下单";
|
case "SYSTEMERROR":
|
return "系统超时,请重新支付";
|
case "OUT_TRADE_NO_USED":
|
return "请勿重复提交该订单";
|
default:
|
return "网络正在开小差,请稍后再试";
|
}
|
}
|
|
}
|