1.开发微信公众号支付准备资料
①APPID,这个数据我们可以从“申请微信支付成功”的邮件中获取。
②AppSecret,这个数据,大家可以看上图中获取。
③Mch_id,这是值是微信支付商户号,大家可以从邮件中获取
④KEY,这个参数KEY是在商户后台配置的一个32位的key,微信商户平台-账户设置-安全设置-api安全,在这里设置。这个值是可以自行设置的。
2.设置支付目录
支付授权目录说明:
1、商户最后请求拉起微信支付收银台的页面地址我们称之为“$\color{red}{支付目录}$”,例如:https://www.weixin.com/pay.php。
2、商户实际的支付目录必须和在微信支付商户平台设置的一致,否则会报错“当前页面的URL未注册:”
支付授权目录设置说明:
登录微信支付商户平台(pay.weixin.qq.com)-->产品中心-->开发配置,设置后一般5分钟内生效。
支付授权目录校验规则说明:
1、如果支付授权目录设置为顶级域名(例如:https://www.weixin.com/ ),那么只校验顶级域名,不校验后缀;
2、如果支付授权目录设置为多级目录,就会进行全匹配,例如设置支付授权目录为https://www.weixin.com/abc/123/,则实际请求页面目录不能为https://www.weixin.com/abc/,也不能为https://www.weixin.com/abc/123/pay/,必须为https://www.weixin.com/abc/123/
具体看官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3
3.首先要看微信支付的业务流程,官方文档——>开发微信支付
微信支付的流程(主要步骤):
1、用户访问微信OAuth2.0网站,通过OAuth2.0的重定向获得code
2、根据code获得用户的标识符openid,这个参数在调用统一下单接口中会用到
3、调用统一下单API获得prepay_id
4、获得prepay_id后,接下来要调用通过“网页端调起支付API”,这个调用的API需要一系列参数,这些参数我们在后台进行组装,通过JSON传到前台去
5、获得数据后通过调用JSPAI发起微信支付
6、用户输入支付密码,支付完成
7、等待微信回调,回调中处理业务
8、支付流程完毕
微信支付所需依赖:
commons-codec
commons-codec
1.6
commons-logging
commons-logging
1.1.3
org.apache.httpcomponents
fluent-hc
4.3.6
org.apache.httpcomponents
httpclient
4.3.6
org.apache.httpcomponents
httpclient-cache
4.3.6
org.apache.httpcomponents
httpcore
4.3.3
org.apache.httpcomponents
httpmime
4.3.6
org.jsoup
jsoup
1.7.2
com.alibaba
fastjson
1.2.1
4、微信支付整体流程实现
1.获取code值,我这边是通过$\color{red}{前端写的js}$来获取code值的
2.通过Code 获取用户 openid 和 access_token
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import ***********************.WeChatConst;//一些常量
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import java.io.*;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
/**
* Created by Arthur on 2015/11/10 16:45.
*/
public class WeChatUtil {
/**
* 通过Code 获取用户 openid 和 access_token
* @param code
* @return
* @throws IOException
*/
public static Map
getOpenIdByCode(String code) throws IOException { /**
* 设置访问路径
*/
HttpPost httppost = new HttpPost("https://api.weixin.qq.com/sns/oauth2/access_token");
/**
* 组装请求参数
*/
String reqEntityStr = "appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
reqEntityStr = reqEntityStr.replace("APPID", WeChatConst.APPID);
reqEntityStr = reqEntityStr.replace("SECRET", WeChatConst.APP_SECRET);
reqEntityStr = reqEntityStr.replace("CODE", code);
StringEntity reqEntity = new StringEntity(reqEntityStr);
/**
* 设置浏览器
*/
DefaultHttpClient httpclient = new DefaultHttpClient();
/**
* 设置参数
*/
httppost.setEntity(reqEntity);
/**
* 发起请求
*/
HttpResponse response = httpclient.execute(httppost);
/**
* 获得请求内容
*/
String strResult = EntityUtils.toString(response.getEntity(), Charset.forName("utf-8"));
/**
* 分析内容
*/
JSONObject jsonObject = (JSONObject) JSON.parse(strResult);
Map
map = new HashMap<>(); map.put("openid",jsonObject.get("openid"));
map.put("access_token",jsonObject.get("access_token"));
map.put("refresh_token",jsonObject.get("refresh_token"));
return map;
}
/**
* 统一下单
* 获得PrePayId
* @param body 商品或支付单简要描述
* @param out_trade_no 商户系统内部的订单号,32个字符内、可包含字母
* @param total_fee 订单总金额,单位为分
* @param IP APP和网页支付提交用户端ip
* @param notify_url 接收微信支付异步通知回调地址
* @param openid 用户openId
* @throws IOException
*/
public static String unifiedorder(String body,String out_trade_no,Integer total_fee,String IP,String notify_url,String openid)throws IOException {
/**
* 设置访问路径
*/
HttpPost httppost = new HttpPost("https://api.mch.weixin.qq.com/pay/unifiedorder");
/**
* 组装请求参数
* 按照ASCII排序
*/
String reqEntityStr = "appid=APPID" +
"&body=BODY" +
"&mch_id=MCH_ID" +
"&nonce_str=NONCE_STR" +
"¬ify_url=NOTIFY_URL" +
"&openid=OPENID" +
"&out_trade_no=OUT_TRADE_NO" +
"&spbill_create_ip=IP" +
"&total_fee=TOTAL_FEE" +
"&trade_type=TRADE_TYPE"
;//这个字段是用于之后MD5加密的,字段要按照ascii码顺序排序
/**
* 设置数据
*/
String nonce_str = getNonceStr().toUpperCase();//随机数据
reqEntityStr = reqEntityStr.replace("APPID", WeChatConst.APPID);
reqEntityStr = reqEntityStr.replace("MCH_ID", WeChatConst.MCH_ID);
reqEntityStr = reqEntityStr.replace("NONCE_STR", nonce_str);
reqEntityStr = reqEntityStr.replace("BODY", body);
reqEntityStr = reqEntityStr.replace("OUT_TRADE_NO", out_trade_no);
reqEntityStr = reqEntityStr.replace("TOTAL_FEE", total_fee.toString());
reqEntityStr = reqEntityStr.replace("IP", IP);
reqEntityStr = reqEntityStr.replace("NOTIFY_URL", notify_url);
reqEntityStr = reqEntityStr.replace("TRADE_TYPE", WeChatConst.TRADE_TYPE);
reqEntityStr = reqEntityStr.replace("OPENID", openid);
/**
* 生成sign的数据
*/
String sign = reqEntityStr + "&key="+WeChatConst.KEY;
sign = MD5Util.MD5(sign).toUpperCase();
/**
* 组装XML
*/
StringBuilder sb = new StringBuilder("");
sb.append("
"); setXmlKV(sb,"appid",WeChatConst.APPID);
setXmlKV(sb,"body",body);
setXmlKV(sb,"mch_id",WeChatConst.MCH_ID);
setXmlKV(sb,"nonce_str",nonce_str);
setXmlKV(sb,"notify_url",notify_url);
setXmlKV(sb,"openid",openid);
setXmlKV(sb,"out_trade_no",out_trade_no);
setXmlKV(sb,"spbill_create_ip",IP);
setXmlKV(sb,"total_fee",total_fee.toString());
setXmlKV(sb,"trade_type",WeChatConst.TRADE_TYPE);
setXmlKV(sb,"sign",sign);
sb.append("");
// reqEntityStr = reqEntityStr.replace("sign", sign);
StringEntity reqEntity = new StringEntity(new String (sb.toString().getBytes("UTF-8"),"ISO8859-1"));//这个处理是为了防止传中文的时候出现签名错误
httppost.setEntity(reqEntity);
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpResponse response = httpclient.execute(httppost);
String strResult = EntityUtils.toString(response.getEntity(), Charset.forName("utf-8"));
// System.out.println(strResult);
return getPrePayId(strResult);
}
/**
* 获得32位随机字符串
* @return
*/
public static String getNonceStr(){
String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
StringBuilder sb = new StringBuilder();
Random rd = new Random();
for(int i = 0 ; i < 32 ; i ++ ){
sb.append(str.charAt(rd.nextInt(str.length())));
}
return sb.toString();
}
/**
* 插入XML标签
* @param sb
* @param Key
* @param value
* @return
*/
public static StringBuilder setXmlKV(StringBuilder sb,String Key,String value){
sb.append("<");
sb.append(Key);
sb.append(">");
sb.append(value);
sb.append("");
sb.append(Key);
sb.append(">");
return sb;
}
/**
* 解析XML 获得 PrePayId
* @param xml
* @return
*/
public static String getPrePayId(String xml){
int start = xml.indexOf("
"); int end = xml.indexOf("");
if(start < 0 && end < 0){
return null;
}
return xml.substring(start + "
".length(),end) .replace("","");
}
}
public interface WeChatConst {
String APPID = "";//填写APPID
String APP_SECRET = "";//填写AppSecret
String MCH_ID = "";//Mch_id,这是值是微信支付商户号,大家可以从邮件中获取
String KEY = "";//这个参数KEY是在商户后台配置的一个32位的key,微信商户平台-账户设置-安全设置-api安全
/**
* 交易类型
*/
String TRADE_TYPE = "JSAPI";
}
我们可以调用里面的“getOpenIdByCode”方法通过code 获得openid,这个openid我们先记录下来,稍后会用到的,接下来我们要经行调用统一下单接口
3.然后通过可以调用里面的“unifiedorder”获取prepay_id
后台调佣统一下单的API,获得prepay_id
**这步我们先要设置支付授权目录**
微信公众平台→微信支付→开发配置→测试授权目录和测试白名单。
直接配置支付授权目录也行。
后台获得到需要用户支付的金额,以及一些列的产品信息后,系统经行预下单,也是就调用调用统一下单的API,生成一个需要支付的订单,获得prepay_id,该值用于在前端页面,通过JSAPI中调用微信支付需要用到,代码看下面的 "JSONObjectdoRecharge(HttpServletRequest request, String code)" 的方法,以及WeChatUtil。
统一下单大家可以调用我刚刚提供给大家的工具类中的WeChatUtil.unifiedorder这个方法,参数在方法上面的注释有写清楚。
这里如果你们想自行封装需要注意一个编码问题,这个会导致你回调产生签名错误。
获得prepay_id后,我们需要在后台组装,调用微信JSAPI的接口数据。
以下是需要组装起来的参数,也是进行微信支付的必须参数
"appId": "", //公众号ID,由商户传入
"timeStamp": "", //时间戳
"nonceStr": "", //随机串
"package": "", //获得PrePayId
"signType": "MD5", //微信签名方式:
"paySign": "" //微信签名
4.前端所需参数,通过这些参数可以进行支付
获取这些参数来源是需要——>进行预下单——>方法如下:
/**
* 微信支付——进行预下单
*
* @param request
* @param code
* @return
* @throws IOException
*/
@RequestMapping(value ="/WxPay")
public static JSONObjectdoRecharge(HttpServletRequest request, String code)throws IOException {
//获取openId的方法
Map openIdByCode = WeChatUtil.getOpenIdByCode(code);
System.out.println("openId:" + openIdByCode.get("openid"));
String body = WeChatConst.BOBY;
//商户订单号
String out_trade_no = UUIDUtils.getUUID().toUpperCase();
System.out.println("商户订单号:" + out_trade_no);
//支付金额
int total_fee =1;
//IP
String IP = WeChatConst.IP;
//支付成功后回调的地址
String openid = (String) openIdByCode.get("openid");
//在业务层调用 WeChatUtil.unifiedorder方法获得prepay_id
String prepay = WeChatUtil.unifiedorder(body, out_trade_no, total_fee, IP, WeChatConst.NOTIFY_URL, openid);
System.out.println("prepay_id" + prepay);
//将参数组装起来
String prepay_id = prepay;
//1970年到现在的秒数
String timeStamp = String.valueOf((System.currentTimeMillis() /1000));
//数据字符串
String nonceStr = WeChatUtil.getNonceStr().toUpperCase();
//prepay_id
String packageStr ="prepay_id=" + prepay_id;
String signType ="MD5";
//签名
String paySign =
"appId=" + WeChatConst.APPID +
"&nonceStr=" + nonceStr +
"&package=prepay_id=" + prepay_id +
"&signType=" + signType +
"&timeStamp=" + timeStamp +
"&key=" + WeChatConst.KEY;//注意这里的参数要根据ASCII码 排序
//将数据MD5加密
paySign = Md5Utils.encrypt(paySign).toUpperCase();
JSONObject object =new JSONObject();
object.put("appId", WeChatConst.APPID);
object.put("timeStamp", timeStamp);
object.put("nonceStr", nonceStr);
object.put("package", packageStr);
object.put("signType", signType);
object.put("paySign", paySign);
return object;
}
通过该方法——>JSONObjectdoRecharge——>获取进行支付参数
5.将这些参数(appId、nonceStr、package、signType、timeStamp、key)填写到如下页面:
5、将该参数设置好的页面HTML放到 "支付授权目录"(授权目录第3步骤有讲到)
6、直接访问该html文件,就可以进行支付了
比如我存放到服务器当中的https://xx.lxx-xx.com/h5/fjyc/wxPay.html 目录
而这个项目当中目录是支付授权目录,那我直接访问该链接(https://xx.lxx-xx.com/h5/fjyc/wxPay.html )