微信支付
一、微信支付的几种方式
刷卡支付:“我”--》“钱包”--》“付款”,扫码机器扫描用户二维码,完成支付
公众号支付:微信公众号内部完成支付功能
扫码支付:微信打开扫一扫,扫描商户二维码,手动转账
APP支付:商户内部集成微信SDK,点击支付跳转微信内部完成支付
二、APP支付实现
A、准备工作
条件:不需要APP开发结束后才申请,开发期间就可以申请
1、登录“微信开放平台”:https://open.weixin.qq.com ,认证开发者资质,个人无法认证,需要企业信息。认证大概时间一周
2、开发者认证通过后,创建应用,需要提供应用Logo、包名(包名与其它应用不能重复),审核大概时间一周;
审核通过后,会获得微信分享功能,微信支付功能需要另外申请;
微信分享功能:包名+应用签名
3、申请微信支付,提供APP截图:首页、支付页、详情页、关于我们、商品页;审核比较严,预留一周时间;
4、审核通过后,账户邮箱会收到“微信商户平台”账户信息,通过提供的商户平台账号和密码登录商户平台,登录需下载安全证书;
获取APP秘钥,APP秘钥需要保存好,忘记只能重新获取。
5、相关参数整理
应用签名:微信开放平台创建应用后,生成应用签名,用于 “微信分享”功能
appid:微信开放平台创建应用后,生成appid,APP的唯一身份标识,“微信分享”、“微信支付”都会使用,无法修改
AppSecret:微信开放平台创建应用后,验证身份后,生成AppSecret,需好好保存,后期登录无法查看,只能重新生成
应用包名:包名必须唯一,不能与其它任何已登录应用平台的程序的包路径重复,否则审核不通过
商户号:”微信支付“申请通过后,收到的邮件中,用于微信支付功能
APP密钥:来源参考目录4,主要用于“微信支付”API中sign的获取
B、开发实现
1、统一下单
微信支付参数配置类
* @author shb * */ public class WeiXinConfig { /** * APPId */ public static String appId = "****"; /** * 微信支付商户号 */ public static String mchId = "****"; /** * 签名秘钥 */ public static String key = "****"; /** * AppSecret */ public static String appSecret = "****"; /** * 交易类型 */ public static String tradsType = "APP"; /** * 如果设置--不能使用信用卡支付 */ public static String limitPay = "no_credit"; /** * 微信支付回调地址 */ public static String notifyUrl = "****";
/**
* 统一下单请求地址
*/
public static String unifiedOrderUrl = "****";
}
统一下单参数实体
import com.guduo.common.utils.UUIDUitl; /** * 统一下单参数 * @author shb * */ public class UnifiedOrderParams { //初始化参数 public UnifiedOrderParams() { setAppid(WeiXinConfig.appId);//APPId setMchid(WeiXinConfig.mchId);//商户号 setNoncestr(UUIDUitl.generateUUID());//随机数 setTradeType(WeiXinConfig.tradsType);//支付类型 setNotifyUrl(WeiXinConfig.notifyUrl);//支付回调地址 } /** * 应用id */ private String appid; /** * 商户 */ private String mchid; /** * 随机字符串 */ private String noncestr; /** * 签名 */ private String sign; /** * 商品描述 */ private String body; /** * 商品订单号 */ private String outTradeNo; /** * 总金额 */ private int totalFee; /** * 终端ip */ private String spdillCreateIp; /** * 回调地址 */ private String notifyUrl; /** * 交易类型 */ private String tradeType; public String getAppid() { return appid; } public void setAppid(String appid) { this.appid = appid; } public String getMchid() { return mchid; } public void setMchid(String mchid) { this.mchid = mchid; } public String getNoncestr() { return noncestr; } public void setNoncestr(String noncestr) { this.noncestr = noncestr; } public String getSign() { return sign; } public void setSign(String sign) { this.sign = sign; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public String getOutTradeNo() { return outTradeNo; } public void setOutTradeNo(String outTradeNo) { this.outTradeNo = outTradeNo; } public int getTotalFee() { return totalFee; } public void setTotalFee(int totalFee) { this.totalFee = totalFee; } public String getSpdillCreateIp() { return spdillCreateIp; } public void setSpdillCreateIp(String spdillCreateIp) { this.spdillCreateIp = spdillCreateIp; } public String getNotifyUrl() { return notifyUrl; } public void setNotifyUrl(String notifyUrl) { this.notifyUrl = notifyUrl; } public String getTradeType() { return tradeType; } public void setTradeType(String tradeType) { this.tradeType = tradeType; } }
统一下单:请求参数封装xml
import org.apache.commons.codec.digest.DigestUtils; import com.guduo.common.utils.UUIDUitl; import com.guduo.common.weixinPay.config.UnifiedOrderReqParams; import com.guduo.common.weixinPay.config.WxConfig; /** * 统一下单:请求参数封装xml * 1、 * @author shb * */ public class UnifiedOrderParamsXml { /** * 封装统一下单请求对象 * @param ip * @param type * @param totalFee * @return */ public static UnifiedOrderReqParams getParams(String ip,String type,int totalFee) { UnifiedOrderReqParams params = new UnifiedOrderReqParams(); params.setBody(type); params.setOutTradeNo(UUIDUitl.generateUUID()); params.setSpdillCreateIp(ip); params.setTotalFee(totalFee); //调用sign方法,获取sign params.setSign(getUnifiedOrderSign(params)); return params; } /** * 统一下单:获取sign * @param params: * @return */ public static String getUnifiedOrderSign(UnifiedOrderReqParams params) { //获取请求字段排序 String stringA = new UnifiedOrderSignXml().getBuffer(params); //拼接API秘钥 String stringSignTemp = stringA+"&key="+WxConfig.key; String sign = DigestUtils.md5Hex(stringSignTemp).toUpperCase(); return sign; } /** * 统一下单请求参数封装 * @param params * @return */ public static String getXmlInfo(UnifiedOrderReqParams params) { //封装xml请求 StringBuilder sb = new StringBuilder(); sb.append("<xml>"); //封装应用id sb.append("<appid>"); sb.append("<![CDATA["); sb.append(params.getAppid()); sb.append("]]>"); sb.append("</appid>"); //封装商品描述 sb.append("<body>"); sb.append("<![CDATA["); sb.append(params.getBody()); sb.append("]]>"); sb.append("</body>"); //封装商户号 sb.append("<mch_id>"); sb.append("<![CDATA["); sb.append(params.getMchid()); sb.append("]]>"); sb.append("</mch_id>"); //封装随机字符串 sb.append("<nonce_str>"); sb.append("<![CDATA["); sb.append(params.getNoncestr()); sb.append("]]>"); sb.append("</nonce_str>"); //封装通知地址 sb.append("<notify_url>"); sb.append("<![CDATA["); sb.append(params.getNotifyUrl()); sb.append("]]>"); sb.append("</notify_url>"); //封装商户订单号 sb.append("<out_trade_no>"); sb.append("<![CDATA["); sb.append(params.getOutTradeNo()); sb.append("]]>"); sb.append("</out_trade_no>"); //封装终端ip sb.append("<spbill_create_ip>"); sb.append("<![CDATA["); sb.append(params.getSpdillCreateIp()); sb.append("]]>"); sb.append("</spbill_create_ip>"); //封装总金额 sb.append("<total_fee>"); sb.append("<![CDATA["); sb.append(params.getTotalFee()); sb.append("]]>"); sb.append("</total_fee>"); //封装交易类型 sb.append("<trade_type>"); sb.append("<![CDATA["); sb.append(params.getTradeType()); sb.append("]]>"); sb.append("</trade_type>"); //封装签名 sb.append("<sign>"); sb.append("<![CDATA["); sb.append(params.getSign()); sb.append("]]>"); sb.append("</sign>"); sb.append("</xml>"); return sb.toString(); } }
统一下单:封装sign
/** * 统一下单:封装sign * @author shb * */ public class UnifiedOrderSignXml { /** * 按照参数名ASCII字典排序请求字段 * @param params * @return */ public String getBuffer(UnifiedOrderReqParams params) { //拼接请求字符串 StringBuffer sb = new StringBuffer(); //封装应用id sb.append("appid="); sb.append(params.getAppid()); sb.append("&"); //封装商品描述 sb.append("body="); sb.append(params.getBody()); sb.append("&"); //封装商户号 sb.append("mch_id="); sb.append(params.getMchid()); sb.append("&"); //封装随机字符串 sb.append("nonce_str="); sb.append(params.getNoncestr()); sb.append("&"); //封装通知地址 sb.append("notify_url="); sb.append(params.getNotifyUrl()); sb.append("&"); //封装商户订单号 sb.append("out_trade_no="); sb.append(params.getOutTradeNo()); sb.append("&"); /*//封装签名 sb.append("sign="); sb.append(params.getSign()); sb.append("&");*/ //封装终端ip sb.append("spbill_create_ip="); sb.append(params.getSpdillCreateIp()); sb.append("&"); //封装总金额 sb.append("total_fee="); sb.append(params.getTotalFee()); sb.append("&"); //封装交易类型 sb.append("trade_type="); sb.append(params.getTradeType()); return sb.toString(); } }
import java.util.HashMap; import java.util.List; import java.util.Map; import org.jdom.Document; import org.jdom.Element; import org.jdom.input.SAXBuilder; import org.xml.sax.InputSource; /** * xml解析工具:针对微信通讯参数要求 * @author shb * */ public class XmlUtil { /** * 获取子节点内容 * @param root * @param elementName * @return */ public static String getChildElement(Element root,String elementName) { Element element = root.getChild(elementName); return element.getText(); } /** * jdom解析xml * @param is * @return */ public static Map<String, String> jdomUtil(InputSource is) { Map<String, String> map = new HashMap<String, String>(); try{ // 创建一个新的SAXBuilder SAXBuilder sb = new SAXBuilder(); // 通过输入源构造一个Document Document doc = sb.build(is); Element root = doc.getRootElement();// 指向根节点 @SuppressWarnings("unchecked") List<Element> es = root.getChildren(); if (es != null && es.size() != 0) { for (Element element : es) { map.put(element.getName(), element.getText()); } } } catch (Exception e) { e.printStackTrace(); } return map; } }
/** * 微信支付工具类 * @author shb * */ public class WxPayUtil { /** * 调用微信统一下单接口 * @param ip:请求终端ip * @param orderType:交易类型 * @param totalFee:交易金额,单位为分 * @return */ public static Map<String,String> unifiedOrderRequest(String ip,String orderType,int totalFee) { CloseableHttpClient httpclient = HttpClients.createDefault(); Map<String, String> map = new HashMap<String, String>(); //获取封装好的请求参数 UnifiedOrderReqParams params = UnifiedOrderParamsXml.getParams(ip, orderType, totalFee); try { HttpPost httppost = new HttpPost(WxConfig.unifiedOrderUrl); StringEntity entity = new StringEntity(UnifiedOrderParamsXml.getXmlInfo(params), "utf-8"); entity.setContentEncoding("UTF-8"); entity.setContentType("application/json"); httppost.setEntity(entity); CloseableHttpResponse result = httpclient.execute(httppost); int code = result.getStatusLine().getStatusCode(); System.out.println(code); String message = EntityUtils.toString(result.getEntity(),"UTF-8"); StringReader sr = new StringReader(message); InputSource is = new InputSource(sr); System.out.println(message); map = XmlUtil.jdomUtil(is); for (Map.Entry<String, String> entry : map.entrySet()) { System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); } } catch (Exception e) { e.printStackTrace(); } finally{ // 关闭连接,释放资源 try { httpclient.close(); } catch (IOException e) { e.printStackTrace(); } } return map; } }
/** * 支付回调返回结果 * @author shb * */ public class NotifyXml { /** * 解析回调返回的数据 * @param body * @return */ public static Map<String, String> getNotifyParams(String body) { Map<String, String> params= new HashMap<String, String>(); Reader io = new StringReader(body); try { Document doc = new SAXBuilder().build(io); Element root = doc.getRootElement(); String resultCode = XmlUtil.getChildElement(root, "result_code");//支付结果 String returnMsg = XmlUtil.getChildElement(root, "return_msg");//返回信息,如非空,为错误原因 String orderNo = XmlUtil.getChildElement(root, "out_trade_no");//系统订单号 String transactionId = XmlUtil.getChildElement(root, "transaction_id");//微信订单号 String returnCode = XmlUtil.getChildElement(root, "return_code");//请求结果 //封装 params.put("resultCode", resultCode); params.put("returnMsg",returnMsg ); params.put("orderNo",orderNo ); params.put("transactionId",transactionId ); params.put("returnCode",returnCode ); } catch (JDOMException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return params; } /** * 封装回调返回结果 * @param body * @return */ public static String notifyReback() { StringBuffer reback = new StringBuffer(); reback.append("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"); return reback.toString(); } public static void main(String[] args) { System.out.println(notifyReback()); } }