一、微信支付的流程图如下(微信官网流程如下):
二、支付步骤如下
1、小程序调用wx.login() 获取 临时登录凭证code ,并发送到开发者服务器(后台代码)。
2、开发者服务器以code换取 用户唯一标识openid 和 会话密钥session_key,通过调用接口来获取,接口名和参数值如下:
接口地址:
https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
参数说明:appid 小程序的唯一标识、secret 小程序的app secret、js_code 为前端传入的code值、grant_type 填写值为authorization_code即可。***注意所有参数都为必填***
后端代码如下:
HttpGet httpGet = new HttpGet("https://api.weixin.qq.com/sns/jscode2session?appid="+Configure.getAppID()+"&secret="+Configure.getSecret()+"&js_code="+code+"&grant_type=authorization_code");
HttpClient httpClient = HttpClients.createDefault();
HttpResponse res = httpClient.execute(httpGet);
HttpEntity entity = res.getEntity();
String result = EntityUtils.toString(entity, "UTF-8");
appid、secret等这样的值一般配置在类中的常量值,方便后续的复用。
3、后台调用统一下单接口
接口地址:URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder
参数列表如官网所示:
除了必填参数外,其他可选参数根据自己的实际情况添加:
//生成商户订单 调用unifiedorder得到 prepay_id
JSONObject json = new JSONObject();;
try {
JSONObject json_test = JSONObject.parseObject(result);
String openid = json_test.getString("openid");
OrderInfo order = new OrderInfo();
order.setAppid(Configure.getAppID());
order.setMch_id(Configure.getMch_id());
order.setNonce_str(RandomStringGenerator.getRandomStringByLength(32));
order.setBody("锦官商城");
order.setOut_trade_no(RandomStringGenerator.getRandomStringByLength(32));
order.setTotal_fee(1);
order.setSpbill_create_ip("111.231.203.XXX");
order.setNotify_url("www.entomb/payResult");//通知地址
order.setTrade_type("JSAPI");
order.setOpenid(openid);
order.setSign_type("MD5");
//生成签名
String sign = Signature.getSign(order);
order.setSign(sign);
String result1 = HttpRequest.sendPost("https://api.mch.weixin.qq.com/pay/unifiedorder", order);
System.out.println("result1111: "+result1);
xStream.alias("xml", OrderReturnInfo.class);
// OrderReturnInfo returnInfo = (OrderReturnInfo)xStream.fromXML(result1);
// result1=result1.toString();
// OrderReturnInfo returnInfo =XStreamTool.fromXML(result1,xStream);
int slen=result1.lastIndexOf(" ");
String prepay_id=result1.substring(slen+20, elen);
// json.put("prepay_id", returnInfo.getPrepay_id());
json.put("prepay_id", prepay_id);
} catch (Exception e) {
e.printStackTrace();
}
注释部分可以根据自己的情况选用,因为我的程序环境问题不能使用该代码得到prepay_id
示例(官网)根据自己的小程序值修改为自己相对应的值:
JSAPI支付测试
***参数的格式为xml格式,且根节点为
调用统一下单接口并传入需求的参数后成功返回的数据和格式为:
4、再次签名(需要返回的值)
try {
System.out.println("Json: "+json);
String repay_id = json.getString("prepay_id");
SignInfo signInfo = new SignInfo();
signInfo.setAppId(Configure.getAppID());
long time = System.currentTimeMillis()/1000;
signInfo.setTimeStamp(String.valueOf(time));
signInfo.setNonceStr(RandomStringGenerator.getRandomStringByLength(32));
signInfo.setRepay_id("prepay_id="+repay_id);
signInfo.setSignType("MD5");
//数据签名
String sign = Signature.getSign(signInfo);
JSONObject json1 = new JSONObject();
json1.put("timeStamp", signInfo.getTimeStamp());
json1.put("nonceStr", signInfo.getNonceStr());
json1.put("package", signInfo.getRepay_id());
json1.put("signType", signInfo.getSignType());
json1.put("appid", Configure.getAppID());
json1.put("paySign", sign);
return json1.toJSONString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
将此部分代码的返回值传递给前端代码即可调用支付(完)
该程序使用的工具类和相关的代码如下:
public class SignInfo {
private String appId;//小程序ID
private String timeStamp;//时间戳
private String nonceStr;//随机串
@XStreamAlias("package")
private String repay_id;
private String signType;//签名方式
//省略get set方法
}
public class OrderInfo {
private String appid;// 小程序ID
private String mch_id;// 商户号
private String nonce_str;// 随机字符串
private String sign_type;//签名类型
private String sign;// 签名
private String body;// 商品描述
private String out_trade_no;// 商户订单号
private int total_fee;// 标价金额 ,单位为分
private String spbill_create_ip;// 终端IP
private String notify_url;// 通知地址
private String trade_type;// 交易类型
private String openid;//用户标识 //省略get set方法}
public class XStreamTool {
public static OrderReturnInfo fromXML(String str,XStream xStream)
{
OrderReturnInfo returnInfo = (OrderReturnInfo)xStream.fromXML(str);
return returnInfo;
}
}
public class OrderReturnInfo {
@XStreamAlias("return_code")
private String return_code;
@XStreamAlias("return_msg")
private String return_msg;
@XStreamAlias("appid")
private String appid;
@XStreamAlias("mch_id")
private String mch_id;
@XStreamAlias("nonce_str")
private String nonce_str;
@XStreamAlias("sign")
private String sign;
@XStreamAlias("result_code")
private String result_code;
@XStreamAlias("prepay_id")
private String prepay_id;
@XStreamAlias("trade_type")
private String trade_type;}
public class Configure {
private static String key = "10e0cc2e07a41d5c535579360f996fb4";
//小程序ID
private static String appID = "wx85907fdbec00a2f7";
//商户号
private static String mch_id = "1498192602";
//
private static String secret = "10e0cc2e07a41d5c535579360f996fb4";
public class HttpRequest {
//连接超时时间,默认10秒
private static final int socketTimeout = 10000;
//传输超时时间,默认30秒
private static final int connectTimeout = 30000;
/**
* post请求
* @throws IOException
* @throws ClientProtocolException
* @throws NoSuchAlgorithmException
* @throws KeyStoreException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
*/
public static String sendPost(String url, Object xmlObj) throws ClientProtocolException, IOException, UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException {
HttpPost httpPost = new HttpPost(url);
//解决XStream对出现双下划线的bug
XStream xStreamForRequestPostData = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
xStreamForRequestPostData.alias("xml", xmlObj.getClass());
//将要提交给API的数据对象转换成XML格式数据Post给API
String postDataXML = xStreamForRequestPostData.toXML(xmlObj);
//得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
StringEntity postEntity = new StringEntity(postDataXML, "UTF-8");
httpPost.addHeader("Content-Type", "text/xml");
httpPost.setEntity(postEntity);
//设置请求器的配置
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
httpPost.setConfig(requestConfig);
HttpClient httpClient = HttpClients.createDefault();
HttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity, "UTF-8");
return result;
}
/**
* 自定义证书管理器,信任所有证书
*
*/
public static class MyX509TrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] arg0, String arg1)
throws java.security.cert.CertificateException {
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] arg0, String arg1)
throws java.security.cert.CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
}}
package lumber.common;
public class MD5 {
private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "a", "b", "c", "d", "e", "f"};
/**
* 转换字节数组为16进制字串
* @param b 字节数组
* @return 16进制字串
*/
public static String byteArrayToHexString(byte[] b) {
StringBuilder resultSb = new StringBuilder();
for (byte aB : b) {
resultSb.append(byteToHexString(aB));
}
return resultSb.toString();
}
/**
* 转换byte到16进制
* @param b 要转换的byte
* @return 16进制格式
*/
private static String byteToHexString(byte b) {
int n = b;
if (n < 0) {
n = 256 + n;
}
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
/**
* MD5编码
* @param origin 原始字符串
* @return 经过MD5加密之后的结果
*/
public static String MD5Encode(String origin) {
String resultString = null;
try {
resultString = origin;
MessageDigest md = MessageDigest.getInstance("MD5");
resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
} catch (Exception e) {
e.printStackTrace();
}
return resultString;
}
}
public class StreamUtil {
public static String read(InputStream is){
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len = 0;
byte[] buffer = new byte[512];
while((len = is.read(buffer)) != -1){
baos.write(buffer, 0, len);
}
return new String(baos.toByteArray(), 0, baos.size(), "utf-8");
}catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
}
public class Signature {
private static final Logger L = Logger.getLogger(Signature.class);
/**
* 签名算法
* @param o 要参与签名的数据对象
* @return 签名
* @throws IllegalAccessException
*/
public static String getSign(Object o) throws IllegalAccessException {
ArrayList list = new ArrayList();
Class cls = o.getClass();
Field[] fields = cls.getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
if (f.get(o) != null && f.get(o) != "") {
String name = f.getName();
XStreamAlias anno = f.getAnnotation(XStreamAlias.class);
if(anno != null)
name = anno.value();
list.add(name + "=" + f.get(o) + "&");
}
}
int size = list.size();
String [] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < size; i ++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += "key=" + Configure.getKey();
System.out.println("签名数据:"+result);
result = MD5.MD5Encode(result).toUpperCase();
return result;
}
public static String getSign(Map map){
ArrayList list = new ArrayList();
for(Map.Entry entry:map.entrySet()){
if(entry.getValue()!=""){
list.add(entry.getKey() + "=" + entry.getValue() + "&");
}
}
int size = list.size();
String [] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < size; i ++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += "key=" + Configure.getKey();
//Util.log("Sign Before MD5:" + result);
result = MD5.MD5Encode(result).toUpperCase();
//Util.log("Sign Result:" + result);
return result;
}
}
public class StreamUtil {
public static String read(InputStream is){
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len = 0;
byte[] buffer = new byte[512];
while((len = is.read(buffer)) != -1){
baos.write(buffer, 0, len);
}
return new String(baos.toByteArray(), 0, baos.size(), "utf-8");
}catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
}