package jframe.pay.wx.http.client;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSON;
import jframe.pay.domain.DevType;
import jframe.pay.domain.PayException;
import jframe.pay.domain.dao.OrderWx;
import jframe.pay.wx.domain.WxConfig;
import jframe.pay.wx.domain.WxFields;
import jframe.pay.wx.http.ClientRequestHandler;
import jframe.pay.wx.http.PackageRequestHandler;
import jframe.pay.wx.http.PrepayIdRequestHandler;
import jframe.pay.wx.http.RequestHandler;
import jframe.pay.wx.http.ResponseHandler;
import jframe.pay.wx.http.util.WxCore;
import jframe.pay.wx.http.util.WxUtil;
/**
* 微信接口方法
*
* @author dzh
* @date Sep 25, 2014 8:38:27 PM
* @since 1.0
*/
public class WxService implements WxFields {
static Logger LOG = LoggerFactory.getLogger(WxService.class);
/**
* 生成预支付订单
*
* @param req
* @return
*/
public static boolean genPrePay(Map<String, String> req, Map<String, Object> rsp) throws Exception {
String out_trade_no = WxUtil.genTradeNo();
req.put(WxFields.F_orderNo, out_trade_no);
// 设置package订单参数
PackageRequestHandler packageReqHandler = new PackageRequestHandler(); // 生成package的请求类
packageReqHandler.setKey(WxConfig.getConf(WxConfig.KEY_PARTNER_KEY));
packageReqHandler.setParameter(WxFields.F_bank_type, "WX");// 银行渠道
packageReqHandler.setParameter(WxFields.F_body, req.get(WxFields.F_payDesc)); // 商品描述
packageReqHandler.setParameter(WxFields.F_notify_url, WxConfig.getConf(WxConfig.KEY_NOTIFY_URL)); // 接收财付通通知的URL
packageReqHandler.setParameter(WxFields.F_partner, WxConfig.getConf(WxConfig.KEY_PARTNER)); // 商户号
packageReqHandler.setParameter(WxFields.F_out_trade_no, out_trade_no); // 商家订单号
packageReqHandler.setParameter(WxFields.F_total_fee, req.get(WxFields.F_payAmount)); // 商品金额,以分为单位
packageReqHandler.setParameter(WxFields.F_spbill_create_ip, req.get(WxFields.F_remoteIp)); // 订单生成的机器IP,指用户浏览器端IP
packageReqHandler.setParameter(WxFields.F_fee_type, "1"); // 币种,1人民币 66
packageReqHandler.setParameter(WxFields.F_input_charset, WxConfig.getConf(WxConfig.KEY_CHARSET)); // 字符编码
// 获取package包
String packageValue = packageReqHandler.getRequestURL();
// 设置获取prepayid支付参数
String noncestr = WxUtil.getNonceStr();
String timestamp = WxUtil.getTimeStamp();
// 获取prepayid的请求类
PrepayIdRequestHandler prepayReqHandler = new PrepayIdRequestHandler();
prepayReqHandler.setParameter(WxFields.F_appid, WxConfig.getConf(WxConfig.KEY_APP_ID));
prepayReqHandler.setParameter(WxFields.F_appkey, WxConfig.getConf(WxConfig.KEY_APP_KEY));
prepayReqHandler.setParameter(WxFields.F_noncestr, noncestr);
prepayReqHandler.setParameter(WxFields.F_package, packageValue);
prepayReqHandler.setParameter(WxFields.F_timestamp, timestamp);
prepayReqHandler.setParameter(WxFields.F_traceid, "");
// 生成获取预支付签名
String sign = prepayReqHandler.createSHA1Sign();
// 增加非参与签名的额外参数
prepayReqHandler.setParameter(WxFields.F_app_signature, sign);
prepayReqHandler.setParameter(WxFields.F_sign_method, WxConfig.getConf(WxConfig.KEY_SIGN_METHOD));
String gateUrl = WxConfig.getConf(WxConfig.KEY_GATE_URL) + "?access_token=" + req.get(WxFields.F_token);
prepayReqHandler.setGateUrl(gateUrl);
// 获取prepayId
String prepayid = prepayReqHandler.sendPrepay();
if (null != prepayid && !"".equals(prepayid)) {
// packageValue = "Sign=" + packageValue; 返回客户端支付参数的请求类
ClientRequestHandler clientHandler = new ClientRequestHandler();
clientHandler.setParameter(WxFields.F_appid, WxConfig.getConf(WxConfig.KEY_APP_ID));
clientHandler.setParameter(WxFields.F_appkey, WxConfig.getConf(WxConfig.KEY_APP_KEY));
clientHandler.setParameter(WxFields.F_noncestr, noncestr);
// if
// (req.get(WxFields.F_devType).equals(DevType.ANDRIOD.toString()))
// {
// clientHandler.setParameter(WxFields.F_package, "Sign=" +
// packageValue);
// } else if
// (req.get(WxFields.F_devType).equals(DevType.IOS.toString())) {
// clientHandler.setParameter(WxFields.F_package, "Sign=WXPay");
// } else {
// throw new PayException("Invalid devType -> " +
// req.get(WxFields.F_devType) + "when genPrePay");
// }
clientHandler.setParameter(WxFields.F_package, "Sign=" + packageValue);
clientHandler.setParameter(WxFields.F_partnerid, WxConfig.getConf(WxConfig.KEY_PARTNER));
clientHandler.setParameter(WxFields.F_prepayid, prepayid);
clientHandler.setParameter(WxFields.F_timestamp, timestamp);
sign = clientHandler.createSHA1Sign();
// 这些是手机端需要的参数
rsp.put(WxFields.F_appid, WxConfig.getConf(WxConfig.KEY_APP_ID));
rsp.put(WxFields.F_partnerid, WxConfig.getConf(WxConfig.KEY_PARTNER));
rsp.put(WxFields.F_prepayid, prepayid);
rsp.put(WxFields.F_noncestr, noncestr);
rsp.put(WxFields.F_timestamp, timestamp);
rsp.put(WxFields.F_packageValue, packageValue);
rsp.put(WxFields.F_payDesc, req.get(WxFields.F_payDesc));
rsp.put(WxFields.F_sign, sign);
return true;
}
return false;
}
public static boolean backPay(Map<String, String> req, Map<String, Object> rsp) {
// 创建支付应答对象
ResponseHandler resHandler = new ResponseHandler(req);
resHandler.setKey(WxConfig.getConf(WxConfig.KEY_PARTNER_KEY));
// 判断签名
if (resHandler.isTenpaySign()) {
// 通知id
String notify_id = resHandler.getParameter(WxFields.F_notify_id);
// 创建请求对象
RequestHandler queryReq = new RequestHandler();
// 通信对象
TenpayHttpClient httpClient = new TenpayHttpClient();
// 应答对象
ClientResponseHandler queryRes = new ClientResponseHandler();
// 通过通知ID查询,确保通知来至财付通
queryReq.init();
queryReq.setKey(WxConfig.getConf(WxConfig.KEY_PARTNER_KEY));
queryReq.setGateUrl("https://gw.tenpay.com/gateway/verifynotifyid.xml");
queryReq.setParameter(WxFields.F_partner, WxConfig.getConf(WxConfig.KEY_PARTNER));
queryReq.setParameter("notify_id", notify_id);
// 通信对象
httpClient.setTimeOut(5);
// 设置请求内容
try {
httpClient.setReqContent(queryReq.getRequestURL());
} catch (UnsupportedEncodingException e) {
LOG.error(e.getMessage());
rsp.put(WxFields.F_result, WxFields.V_Fail);
return false;
}
// 后台调用
if (httpClient.call()) {
// 设置结果参数
try {
queryRes.setContent(httpClient.getResContent());
} catch (Exception e) {
LOG.error(e.getMessage());
rsp.put(WxFields.F_result, WxFields.V_Fail);
return false;
}
queryRes.setKey(WxConfig.getConf(WxConfig.KEY_PARTNER_KEY));
rsp.putAll(queryRes.getAllParameters());
// 获取返回参数
String retcode = queryRes.getParameter("retcode");
String trade_state = queryRes.getParameter("trade_state");
String trade_mode = queryRes.getParameter("trade_mode");
// 判断签名及结果
if (queryRes.isTenpaySign() && "0".equals(retcode) && "0".equals(trade_state)
&& "1".equals(trade_mode)) {
rsp.put(WxFields.F_result, WxFields.V_Success);
rsp.put(WxFields.F_transactionId, queryRes.getParameter("transaction_id"));
return true;
} else {
// 错误时,返回结果未签名,记录retcode、retmsg看失败详情。
// System.out.println("查询验证签名失败或业务错误");
// System.out.println("retcode:"
// + queryRes.getParameter("retcode") + " retmsg:"
// + queryRes.getParameter("retmsg"));
}
} else {
// System.out.println("后台调用通信失败");
// System.out.println(httpClient.getResponseCode());
// System.out.println(httpClient.getErrInfo());
}
} else {
// System.out.println("通知签名验证失败");
}
return false;
}
/**
* 请求地址: https://api.weixin.qq.com/pay/orderquery?access_token=xxxxxx
*
* 请求数据: { "appid" : "wwwwb4f85f3a797777", "package" :
* "out_trade_no=11122&partner=1900090055&sign=4e8d0df3da0c3d0df38f",
* "timestamp" : "1369745073", "app_signature" :
* "53cca9d47b883bd4a5c85a9300df3da0cb48565c", "sign_method" : "sha1" }
*
* 响应格式: { "errcode": 0, "errmsg": "ok", "order_info": { "ret_code": 0,
* "ret_msg": "", "input_charset": "GBK", "trade_state": "0", "trade_mode":
* "1", "partner": "1234567890", "bank_type": "CMB_FP", "bank_billno":
* "201405273540085997", "total_fee": "1", "fee_type": "1",
* "transaction_id": "1218614901201405273313473135", "out_trade_no":
* "JfuKdiBig4zZnE4n", "is_split": "false", "is_refund": "false", "attach":
* "", "time_end": "20140527194139", "transport_fee": "0", "product_fee":
* "1", "discount": "0", "rmb_total_fee": "" } }
*
* 参考: http://www.cnblogs.com/txw1958/p/wxpay-order-query.html
*
* @param req
* @param resp
* @return
* @throws Exception
*/
public static String queryPrePay(Map<String, String> req) throws Exception {
String orderNo = req.get(WxFields.F_orderNo);
String timestamp = req.get(WxFields.F_timestamp);
String token = req.get(WxFields.F_token);
// packageValue
PackageRequestHandler packageReqHandler = new PackageRequestHandler();
packageReqHandler.setKey(WxConfig.getConf(WxConfig.KEY_PARTNER_KEY));
packageReqHandler.setParameter(WxFields.F_out_trade_no, orderNo);
packageReqHandler.setParameter(WxFields.F_partner, WxConfig.getConf(WxConfig.KEY_PARTNER));
String packageValue = packageReqHandler.getRequestURL();
// app_signature
PrepayIdRequestHandler reqHandler = new PrepayIdRequestHandler();
reqHandler.setParameter(WxFields.F_appid, WxConfig.getConf(WxConfig.KEY_APP_ID));
reqHandler.setParameter(WxFields.F_appkey, WxConfig.getConf(WxConfig.KEY_APP_KEY));
reqHandler.setParameter(WxFields.F_package, packageValue);
// yyyyMMddHHmmss
reqHandler.setParameter(WxFields.F_timestamp, timestamp);
String appsign = reqHandler.createSHA1Sign();
// postdata
Map<String, String> postData = new HashMap<String, String>();
postData.put(WxFields.F_appid, WxConfig.getConf(WxConfig.KEY_APP_ID));
postData.put(WxFields.F_package, packageValue);
postData.put(WxFields.F_timestamp, timestamp);
postData.put(WxFields.F_app_signature, appsign);
postData.put(WxFields.F_sign_method, WxFields.V_SHA1);
// 通信对象
TenpayHttpClient httpClient = new TenpayHttpClient();
httpClient.setTimeOut(5);
httpClient.callHttpPost("https://api.weixin.qq.com/pay/orderquery?access_token=" + token,
JSON.toJSONString(postData));
return httpClient.getResContent();
}
/**
* TODO 根据收到的请求数据,解析成map
*
* @param content
* @param req
* @throws Exception
*/
public static void verifyResponse(String content, Map<String, String> req) {
try {
req = WxCore.parseQString(content);
} catch (UnsupportedEncodingException e) {
LOG.error("Parse wx response error {}", content);
}
}
/**
* 退款
*
* @param pay
* @param respMap
* @return
* @throws Exception
*/
public static boolean refund(OrderWx pay, Map<String, String> rspMap) throws Exception {
String out_refund_no = WxUtil.genTradeNo();
rspMap.put("out_refund_no", out_refund_no);
// 商户号
String partner = WxConfig.getConf(WxConfig.KEY_PARTNER);
// 密钥
String key = WxConfig.getConf(WxConfig.KEY_PARTNER_KEY);
// 创建查询请求对象
RequestHandler reqHandler = new RequestHandler();
// 通信对象
TenpayHttpClient httpClient = new TenpayHttpClient();
// 应答对象
ClientResponseHandler resHandler = new ClientResponseHandler();
// -----------------------------
// 设置请求参数
// -----------------------------
reqHandler.init();
reqHandler.setKey(key);
reqHandler.setGateUrl("https://mch.tenpay.com/refundapi/gateway/refund.xml");
// -----------------------------
// 设置接口参数
// -----------------------------
reqHandler.setParameter("service_version", "1.1");
reqHandler.setParameter("partner", partner);
reqHandler.setParameter("out_trade_no", pay.orderNo);
reqHandler.setParameter("transaction_id", pay.transactionId);
reqHandler.setParameter("out_refund_no", out_refund_no);
reqHandler.setParameter("total_fee", String.valueOf(pay.payAmount));
reqHandler.setParameter("refund_fee", String.valueOf(pay.payAmount));
reqHandler.setParameter("op_user_id", partner);
// 操作员密码,MD5处理
reqHandler.setParameter("op_user_passwd", key);
reqHandler.setParameter("recv_user_id", pay.recvUserId);
reqHandler.setParameter("recv_user_name", pay.recvUserName);
// -----------------------------
// 设置通信参数
// -----------------------------
// 设置请求返回的等待时间
httpClient.setTimeOut(5);
// 设置ca证书
// httpClient.setCaInfo(new File("e:/cacert.pem"));
// 设置个人(商户)证书
// httpClient.setCertInfo(new File("e:/1900000109.pfx"), "1900000109");
// 设置发送类型POST
httpClient.setMethod("POST");
// 设置请求内容
String requestUrl = reqHandler.getRequestURL();
httpClient.setReqContent(requestUrl);
String rescontent = "null";
// 后台调用
if (httpClient.call()) {
// 设置结果参数
rescontent = httpClient.getResContent();
resHandler.setContent(rescontent);
resHandler.setKey(key);
// 获取返回参数
String retcode = resHandler.getParameter("retcode");
// 判断签名及结果
if (resHandler.isTenpaySign() && "0".equals(retcode)) {
/*
* 退款状态 refund_status 4,10:退款成功。 3,5,6:退款失败。 8,9,11:退款处理中。 1,2:
* 未确定,需要商户原退款单号重新发起。
* 7:转入代发,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,资金回流到商户的现金帐号
* ,需要商户人工干预,通过线下或者财付通转账的方式进行退款。
*/
String refund_status = resHandler.getParameter("refund_status");
String out_trade_no = resHandler.getParameter("out_trade_no");
rspMap.put("refund_status", refund_status);
rspMap.put(WxFields.F_orderNo, out_trade_no);
return true;
} else {
// 错误时,返回结果未签名,记录retcode、retmsg看失败详情。
rspMap.put(WxFields.F_rspCode, resHandler.getParameter("retcode"));
rspMap.put(WxFields.F_rspDesc, resHandler.getParameter("retmsg"));
}
} else {
LOG.error("后台调用通信失败 http rspCode->{} errInfo->{}", httpClient.getResponseCode(), httpClient.getErrInfo());
// 有可能因为网络原因,请求已经处理,但未收到应答。
}
return false;
// 获取debug信息,建议把请求、应答内容、debug信息,通信返回码写入日志,方便定位问题
// System.out.println("http res:" + httpClient.getResponseCode() + "," +
// httpClient.getErrInfo());
// System.out.println("req url:" + requestUrl);
// System.out.println("req debug:" + reqHandler.getDebugInfo());
// System.out.println("res content:" + rescontent);
// System.out.println("res debug:" + resHandler.getDebugInfo());
}
}