不多废话直接开始今天的微信篇 其实对于微信来说 比支付宝是要稍微麻烦一点的 微信的基本单位是分 其实再这里我也推荐大家数据库使用的时候 用分来做金额单位 好处不少 1:不用担心精度丢失 Double的精度丢失 相信大家都知道 2:不用担心小数点的保留 不用那么多的数据操作 就是大家在使用的时候别搞错了单位 损失永远是自己的 微信支付还又一个比较安全一点的东西就是证书的安装 证书需要到自己的商铺下去下载 对于企业打款和原路返回都需要这个东西 还有这个东西最好放到一个安全的地方 不要放到项目中或则可以下载到的地方 不然你懂的
好的 照例文档我就不准备了 相信大家都看过 直接开始源码
微信支付接口
/**
* 支付前调用,调用后,App拿着该接口的返回值参数去调取微信,发出支付请求
*
* @return
*/
@RequestMapping(value = "/fitPayWx", method = RequestMethod.POST,produces = "application/json;charset=UTF-8")
@Transactional(readOnly = true)
public @ResponseBody ResultModel fitPayWx(HttpServletRequest request, final PayModelVo payModelVo, final String addressId, final String cartIds, String mergeId, final String isDispatch, final String isMerge, final String logisticsId, String ProductList) {
String token = request.getHeader("token");
ResultModel resultModel = new ResultModel();
try {
if(payModelVo.getOutTradeNo()==null){
payModelVo.setOutTradeNo(StrUtils.uuid());
}
String money = payModelVo.getMoney(); /// 变动金额
String type = payModelVo.getType(); // 消费类型 用户充值 用户提现 商品出售 会员购买会员升级 询价单购买 待支付订单的支付(此类支付不需要预订单了)
String paytype = "2"; // 支付方式 1:支付宝 2:微信 3:钱包 4,:额度,5,快捷支付
payModelVo.setIncometype("2");// 1---收入,2---支出
String out_trade_no = payModelVo.getOutTradeNo();
payModelVo.setIncometype("1");
payModelVo.setPaytype(paytype);
String ip = "127.0.0.1";
String conreason = payModelVo.getConreason();// 资金变动说明
String detailid = payModelVo.getDetailid();// 订单id 或者 充值记录id
// (关联性id、与对应的业务表主键id关联即可)
// 3:银联
// 4:钱包(充值的时候是充值方式、购买的时候是购买方式、收入的时候,写成钱包即可)
String productId = "111112"; //PC封装必须
Map<String, String> mapParam = new HashMap<String, String>();
mapParam.put("money", money);
mapParam.put("type", type);
mapParam.put("conreason", conreason);
mapParam.put("product_id",productId);
mapParam.put("detailid", detailid == null ? "" : detailid);
mapParam.put("paytype", paytype);
mapParam.put("incometype", payModelVo.getIncometype());
mapParam.put("business", payModelVo.getBusiness());
//将payModelVo中所有信息存入redis
// RedisTool.setex("notify_"+out_trade_no,Bhyx.getPayresultExistTime(),JSON.toJSONString(payModelVo));
// XX 订单状态未校验
/**
* 封装微信请求参数(统一下单,的预订单信息。获取返回值,将返回值给App用来调出支付页面)
*/
try {
HashMap<String, String> reqMap = new HashMap<String, String>();
reqMap.put("appid", WXPayConfigImpl.getInstance().getAppID());
reqMap.put("mch_id", WXPayConfigImpl.getInstance().getMchID());
reqMap.put("nonce_str", WXPayUtil.generateUUID());
// reqMap.put("detail", WeiChartConfig.subject); //非必填
// 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
reqMap.put("out_trade_no", out_trade_no); // 商户系统内部的订单号,
reqMap.put("fee_type", WXPayConstants.FEE_TYPE);// 非必填
// 符合ISO
// 4217标准的三位字母代码,默认人民币:CNY
reqMap.put("total_fee", TaobusinessUtils.moneyUtilToFen(payModelVo.getMoney())); // 订单总金额,单位为分
// reqMap.put("total_fee",MoneyUtils.moneyYuanToFen("0.01"));
// //订单总金额,单位为分
reqMap.put("spbill_create_ip", ip); // 用户端实际ip
reqMap.put("time_start", String.valueOf(WXPayUtil.getCurrentTimestamp())); // 交易起始时间
// 非必填
// reqMap.put("time_expire", "172.16.40.18"); //交易结束时间
// 非必填
// reqMap.put("goods_tag", "172.16.40.18"); //商品标记 非必填
reqMap.put("notify_url", WXPayConstants.NOTIFY_URL); // 回调地址
reqMap.put("trade_type", payModelVo.getPayRole()); // 交易类型
reqMap.put("body","支付说明");
reqMap.put("limit_pay", "no_credit"); // 指定支付方式,no_credit
// 指定不能使用信用卡支
// 非必填
reqMap.put("sign", WXPayUtil.generateSignature(reqMap, WXPayConfigImpl.getInstance().getKey()));// 生成签名
reqMap.put("product_id",productId);
/**
* 调取微信统一下单
*/
WXPay wxpay = new WXPay(WXPayConfigImpl.getInstance());
Map<String, String> data = wxpay.unifiedOrder(reqMap);
/**
* 判断是否下单成功
*/
if (data.containsKey("return_code") && WXPayConstants.SUCCESS.equals(data.get("return_code"))) {
data.put("time_start", reqMap.get("time_start"));
MyWxPay wxPay = new MyWxPay();
Map<String, String> map = wxPay.doAppUnifiedOrder(data);
map.put("orderNums",out_trade_no);
resultModel = new ResultModel(200,"10104","预下单成功");
resultModel.setResult(map);
} else {
resultModel = new ResultModel(500,"10105","预下单失败");
resultModel.setResult(data.get("return_msg"));
}
}catch (Exception e){
}
} catch (Exception e) {
log.error("微信支付异常", e);
e.printStackTrace();
resultModel = new ResultModel(500,"10107","微信订单异常");
}
log.error("微信支付:" + resultModel.getMsg());
return resultModel;
}
需要注意下 paytype这个参数 他是区别与APP和PC的参数 APP:APP PC:NATIVE 唯一的区别是 返回值中有个url 再PC支付的时候 PC可以直接进行二维码的生成
微信回调
/**
* @Title: notify
* @Description: TODO(微信回调,微信充值 回调,App支付成功后,微信服务器会访问这个接口八次来给后台确认支付成功)
* @date 2018-2-6 下午2:12:08
* @param rep
*/
@RequestMapping(value = "/notify", produces = "application/json;charset=UTF-8")
@Transactional(readOnly = true)
public void wx_notifyA(HttpServletRequest req, HttpServletResponse rep) {
log.info("微信支付回调开始---------------notify()---------------->");
String rtn = "";
String xml = "";
String total_fee = "";
String outtradeno = "";
InputStream inputStream;
Map<String, String> resultMap = new HashMap<String, String>();
try {
inputStream = req.getInputStream();
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
resultMap = WXPayUtil.xmlToMap(document.asXML());
outtradeno = resultMap.get("out_trade_no");
PayModelVo paymodel = (PayModelVo) new Gson().fromJson(RedisTool.get("notify_"+outtradeno), PayModelVo.class);
// 验证签名是否正确
if (WXPayUtil.isSignatureValid(resultMap, WXPayConfigImpl.getInstance().getKey())) {
rtn = resultMap.get("result_code");
total_fee = resultMap.get("total_fee");
if (rtn != null && "SUCCESS".equals(rtn)) {
xml = "<xml><return_code>SUCCESS</return_code><return_msg>OK</return_msg></xml>";
// 数据锁进行并发控制,以避免函数重入造成的数据混乱
synchronized (outtradeno.intern()) {
// 是否已经回调,避免重复查询数据库
// 添加支付状态的缓存
if (RedisTool.exists("notify_" + outtradeno)) {
//查询数据库是否已经充值成功避免重复充值(本次回调访问不一定是微信第几次访问的)
// WalletTran walletTrans = walletTranMapper.findAllByNo(outtradeno);
// 判断该充值详情是否已经存在,不存在则继续
// if (walletTrans == null) {
if (true) {
// 更新业务数据
String str = TaobusinessUtils.moneyUtilToFen(paymodel.getMoney());
if(!total_fee.equals(str)){
//金额数目不对
xml = "<xml><return_code>FAIL</return_code><return_msg>应支付金额和实际金额不符</return_msg></xml>";
writeMessageToResponse(rep, xml);
return;
}
/**
* TODO 回调成功 开始实现业务
*/
// ResultModel resultModel = new ResultModel();
// payService.business(paymodel,resultModel);
if(paymodel.getPaytype().equals("7")){//通联微信购买,需要向pc推送
try {
goeasy(paymodel,"true");
} catch (Exception e) {
e.printStackTrace();
}
}
xml = "<xml><return_code>SUCCESS</return_code><return_msg>OK</return_msg></xml>";
RedisTool.expire("WX" + outtradeno, 60 * 60 * 2);
writeMessageToResponse(rep, xml);
log.info("微信返回值"+JsonUtil.toJson(rep));
log.info("微信支付回调结束---------------notify()---------------->");
return;
}else {
log.info("微信重复调用!!!");
xml = "<return_code><![CDATA[SUCCESS]]></return_code>";
}
RedisTool.expire("WX" + outtradeno, 60 * 60 * 2);
}
}
} else {
xml = "<xml><return_code>FAIL</return_code><return_msg>" + resultMap.get("err_code_des")
+ "</return_msg></xml>";
log.error("执行了微信支付回调" + resultMap.get("err_code_des"));
}
} else {
xml = "<xml><return_code>FAIL</return_code><return_msg>签名验证失败!</return_msg></xml>";
log.error("执行了微信支付回调,签名验证失败!");
}
// 同步微信通知
} catch (Exception e) {
xml = "<xml><return_code>FAIL</return_code><return_msg>" + resultMap.get("err_code_des")
+ "</return_msg></xml>";
RedisTool.setex("notify_" + outtradeno, DateTools.getDatelinetoInt(),"业务处理失败");
log.error("执行了微信支付回调,抛出异常!", e);
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
writeMessageToResponse(rep, xml);
log.info("微信返回值"+JsonUtil.toJson(rep));
log.info("微信支付回调结束---------------notify()---------------->");
}
工具类
WXPayConfigImpl
package com.bing.wxPay.sdk;
import java.io.InputStream;
/**
* 配置文件
*/
public class WXPayConfigImpl implements com.github.wxpay.sdk.WXPayConfig{
private static WXPayConfigImpl INSTANCE;
public static WXPayConfigImpl getInstance() throws Exception {
if (INSTANCE == null) {
synchronized (WXPayConfigImpl.class) {
if (INSTANCE == null) {
INSTANCE = new WXPayConfigImpl();
}
}
}
return INSTANCE;
}
public String getAppID() {
return ""; //appid
}
public String getMchID() {
return ""; //商户号
}
public String getKey() {
return ""; //API密钥
}
public String getSSLPath(){
return "apiclient_cert.p12";
}//证书所在服务器的盘符路径
public String getPosUrl(){
return "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
}
public String getReFulUrl(){
return "https://api.mch.weixin.qq.com/secapi/pay/refund";
}
public int getHttpConnectTimeoutMs() {
return 2000;
}
public int getHttpReadTimeoutMs() {
return 10000;
}
public IWXPayDomain getWXPayDomain() {
return WXPayDomainSimpleImpl.instance();
}
public String getPrimaryDomain() {
return "api.mch.weixin.qq.com";
}
public String getAlternateDomain() {
return "api2.mch.weixin.qq.com";
}
public int getReportWorkerNum() {
return 1;
}
public int getReportBatchSize() {
return 2;
}
@Override
public InputStream getCertStream() {
// TODO Auto-generated method stub
return null;
}
}
WXPayUtil
package com.bing.wxPay.sdk;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.*;
import java.security.MessageDigest;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
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 com.github.wxpay.sdk.WXPayConstants.SignType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WXPayUtil {
public static final Logger log = LoggerFactory.getLogger(WXPayUtil.class);
/**
* 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, 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, 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, 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, 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, SignType.MD5);
}
/**
* 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
*
* @param data 待签名数据
* @param key API密钥
* @param signType 签名方式
* @return 签名
*/
public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
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 (SignType.MD5.equals(signType)) {
return MD5(sb.toString()).toUpperCase();
}
else if (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);
}
}
MyWxPay
package com.bing.wxPay.servcie;
import java.util.Date;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import com.bing.wxPay.sdk.WXPayConfigImpl;
import com.bing.wxPay.sdk.WXPayConstants;
import com.bing.wxPay.sdk.WXPayUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.github.wxpay.sdk.WXPay;
/**
* @author Administrator
* @date 2016.9.9
* @since JDK 1.7
*
* WxPay: 微信支付业务
*/
@Component
public class MyWxPay {
protected static final Logger logger = LoggerFactory.getLogger(MyWxPay.class);
private WXPay wxpay;
private WXPayConfigImpl config;
public MyWxPay() throws Exception {
config = WXPayConfigImpl.getInstance();
wxpay = new WXPay(config);
}
/**
* @Title: doWebUnifiedOrder
* @Description: TODO(网页扫码支付)
* @param data
* @return
*/
public Map<String, String> doWebUnifiedOrder(Map<String, String> data) {
data.put("fee_type", WXPayConstants.FEE_TYPE);
data.put("notify_url", WXPayConstants.NOTIFY_URL);
data.put("time_start", String.valueOf(WXPayUtil.getCurrentTimestamp()));
try {
Map<String, String> r = wxpay.unifiedOrder(data);
r.put("timestamp", data.get("time_start"));
return r;
} catch (Exception e) {
return null;
}
}
/**
* @Title: doAppUnifiedOrder
* @Description: TODO(app微信支付)
* @param data
* @return
*/
public Map<String, String> doAppUnifiedOrder(Map<String, String> data) {
// data.put("fee_type", WXPayConstants.FEE_TYPE);
// data.put("notify_url", WXPayConstants.NOTIFY_URL);
// data.put("time_start", String.valueOf(WXPayUtil.getCurrentTimestamp()));
try {
//重新生成新的签名 返回客户端APP
SortedMap<String, String> signData = new TreeMap<String, String>();
if(data.get("trade_type").equals("NATIVE")){
signData.put("code_url",data.get("code_url"));
}
signData.put("appid", data.get("appid"));
signData.put("partnerid",data.get("mch_id"));
signData.put("prepayid", data.get("prepay_id"));
signData.put("package","Sign=WXPay");
signData.put("noncestr", data.get("nonce_str"));
String timestamp = String.valueOf(new Date().getTime());
signData.put("timestamp", timestamp.substring(0, timestamp.length() - 3));
String paySign = WXPayUtil.generateSignature(signData, config.getKey());
signData.put("sign", paySign);
return signData;
} catch (Exception e) {
return null;
}
}
}
OK 其实微信支付和支付宝支付的逻辑相差不大 差的就是对与代码的理解 有问题欢迎评论
下一篇:微信企业打款