微信呼叫H5支付
一:參考網站
微信開發文件:https://pay.weixin.qq.com/wiki/doc/api/index.html
二:準備
去商戶平臺獲取商戶賬戶id(appid)、微信支付商戶號、金鑰
微信支付閘道器:https://api.mch.weixin.qq.com/pay/unifiedorder
三:配置檔案yml
在yml檔案中配置微信付款需要的引數
#微信支付配置
wechat:
#商戶賬號id
appId: xxxxxxxxxx
#微信支付商戶號
mcHid: xxxxxxxx
#微信支付非同步回撥地址
notifyUrl: xxxxxxxxxxxxxxxxxxxxxxx
#微信退款結果通知地址
notifyRefundUrl: xxxxxxxxxxxxxxxxx
#金鑰
wechatPrivateKey: xxxxxxx
#商品描述
body: xxxxxxxxx
#交易型別
tradeType: MWEB
openId:
#微信支付閘道器
weChatPrepayUrl: https://api.mch.weixin.qq.com/pay/unifiedorder
#微信退款閘道器
weChatRefundPrepayUrl: https://api.mch.weixin.qq.com/secapi/pay/refund
#微信付款查詢訂單閘道器
weChatQueryPrepayUrl: https://api.mch.weixin.qq.com/pay/orderquery
#微信退款查詢閘道器
weChatQueryRefundPrepayUrl: https://api.mch.weixin.qq.com/pay/refundquery
#微信瀏覽器內調起獲取openid時的appid
jsapiAppId: xxxxxxxxxxxx
#JSAPI交易型別
jsapiTradeType: JSAPI
#appSecret
appSecret: xxxxxxxxxxxxxxx
通過實體類可以獲取yml檔案配置的引數值:
package fly.system.bms.entity.input;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @ProjectName: fly-system-bms
* @Package: fly.system.bms.entity.input
* @ClassName: WeChatConfig
* @Author: hfq
* @Description: 微信支付的配置類
* @Date: 2020/9/4 14:13
* @Version: 1.0
*/
@Component
@Data
@ConfigurationProperties(prefix="wechat") //接收application.yml中的weChat下面的屬性
public class WeChatConfig {
//微信賬戶id
private String appId;
//微信支付商戶號
private String mcHid;
//非同步接收微信支付結果通知的回撥地址
private String notifyUrl;
//金鑰
private String wechatPrivateKey;
//商品描述
private String body;
//交易型別
private String tradeType;
private String openId;
//退款結果通知url
private String notifyRefundUrl;
//微信支付閘道器
private String weChatPrepayUrl;
//微信退款閘道器
private String weChatRefundPrepayUrl;
//微信付款查詢訂單閘道器
private String weChatQueryPrepayUrl;
//微信退款查詢閘道器
private String weChatQueryRefundPrepayUrl;
//微信瀏覽器內調起獲取openid時的appid
private String jsapiAppId;
//JSAPI交易型別
private String jsapiTradeType;
//appSecret
private String appSecret;
}
四、呼叫第三方介面
private String weCateApiPay(BigDecimal amount) {
String outTradeNo = OrderCodeFactory.getOrderCode(15L);
//使用者真實ip:H5支付要求商戶在統一下單介面中上傳使用者真實ip地址“spbill_create_ip”
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String ip = SpbillGreateIp.getIpAddress(request);
//獲取商品描述:body
String body = weChatConfig.getBody();
//交易型別
String tradeType = weChatConfig.getTradeType();
String openid = weChatConfig.getOpenId();
//微信支付分配的公眾賬號ID(企業號corpid即為appid)
String appid = weChatConfig.getAppId();
//微信支付分配的商戶號
String mchId = weChatConfig.getMcHid();
//非同步接收微信支付結果的回撥地址
String NnotifyUrl = weChatConfig.getNotifyUrl();
//金鑰
String wechatPlatformApiKey = weChatConfig.getWechatPrivateKey();
//微信支付閘道器
String weChatPrepayUrl = weChatConfig.getWeChatPrepayUrl();
//呼叫微信支付的介面
Map<String, String> map = WechatPayUtils.sendPayment(body, outTradeNo, amount, ip, tradeType,
openid, NnotifyUrl, null, null, appid, mchId, wechatPlatformApiKey,weChatPrepayUrl);
log.info("呼叫微信統一下單返回引數 {}",JSON.toJSONString(map));
//處理業務
//將微信返回的map集合中的地址返回給前臺
return map.get("mweb_url");
}
將呼叫第三方介面返回的地址返回給前端,前端可以通過這個url調起微信支付頁面
注:這個url必須在瀏覽器內開啟,無法直接在微信中開啟支付頁面(微信H5支付的特性)
return_code為FAIL的時候代表未調同第三方支付介面,可以通過return_msg查詢到報錯資訊
result_code為FAIL的時候代表呼叫第三方支付介面出錯,可以通過err_code和err_code_des查詢到出錯原因
WechatPayUtils
package fly.system.bms.utils;
import com.alibaba.fastjson.JSON;
import fly.cloud.common.base.enums.BusinessCodeEnum;
import fly.cloud.common.base.exception.BusinessException;
import fly.system.bms.entity.db.RefundInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
import java.io.*;
import java.math.BigDecimal;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.util.*;
/**
* @ClassName WechatPayUtils
* @Description TODO 微信支付工具類
* @Author liushan
* @Date 2020/1/3 13:39
**/
@Slf4j
public class WechatPayUtils {
// JSAPI支付
public static final String WECHAT_JSAPI = "JSAPI";
// H5支付
public static final String WECHAT_MWEB = "MWEB";
//微信退款
public static final String WECHAT_RETUND = "RETUND";
//微信付款查詢
public static final String WECHAT_PAY_QUERY = "PAYQUERY";
//微信退款查詢
public static final String WECHAT_RETUND_QUERY = "RETUNDQUEY";
//微信回撥簽名key
private static final String FIELD_SIGN = "sign";
/*
* 發起支付請求 body 商品描述 out_trade_no 訂單號 total_fee 訂單金額 單位 元 product_id 商品ID
* appid:微信支付分配的公眾賬號ID(企業號corpid即為此appId)
* mchid:微信支付分配的商戶號
* openid:rade_type=JSAPI時(即JSAPI支付),此引數必傳,此引數為微信使用者在商戶對應appid下的唯一標識。
* notify_url:非同步接收微信支付結果通知的回撥地址,通知url必須為外網可訪問的url,不能攜帶引數
* sign:通過簽名演算法計算得出的簽名值
* trade_type:交易型別
* spbill_create_ip:支援IPV4和IPV6兩種格式的IP地址。使用者的客戶端IP
*/
public static Map<String, String> sendPayment(String body,
String out_trade_no,
BigDecimal total_fee,
String ip,
String trade_type,
String openId,
String wechatNotifyUrl,
String wechatAppId,
String wechatMchId,
String wechatPlatformAppId,
String wechatPlatformMchId,
String wechatPlatformApiKey,
String weChatPrepayUrl) {
String xml = wXParamGenerate(body, out_trade_no, total_fee, ip, trade_type, openId,
wechatNotifyUrl, wechatAppId, wechatMchId, wechatPlatformAppId, wechatPlatformMchId, wechatPlatformApiKey);
String res = httpsRequest(weChatPrepayUrl, "POST", xml);
Map<String, String> data = null;
try {
data = doXMLParse(res);
} catch (Exception e) {
}
return data;
}
// 微信統一下單引數設定
private static String wXParamGenerate(String description,
String outTradeNo,
BigDecimal total_fee,
String ip,
String trade_type,
String openId,
String wechatNotifyUrl,
String wechatAppId,
String wechatMchId,
String wechatPlatformAppId,
String wechatPlatformMchId,
String wechatPlatformApiKey
) {
int fee = total_fee.multiply(new BigDecimal("100"))
.setScale(0, BigDecimal.ROUND_HALF_UP)
.intValue();
Map<String, String> param = new HashMap<String, String>();
param.put("nonce_str", UUID.randomUUID().toString().replace("-", ""));
param.put("body", description);
param.put("out_trade_no", outTradeNo);
param.put("total_fee", fee + "");
param.put("sign_type", "MD5");
param.put("notify_url", wechatNotifyUrl);
// 交易型別
param.put("trade_type", trade_type);
if (WECHAT_JSAPI.equals(trade_type)) {
param.put("openid", openId);
param.put("appid", wechatAppId);
param.put("mch_id", wechatMchId);
param.put("spbill_create_ip", ip);
} else {
param.put("appid", wechatPlatformAppId);
param.put("mch_id", wechatPlatformMchId);
if (WECHAT_MWEB.equals(trade_type)) {
param.put("spbill_create_ip", ip);
}
}
param.put(
"scene_info",
"{\"h5_info\":{\"type\":\"Wap\",\"wap_url\":\"http://" + "www.xueguoxue.com" + "\",\"wap_name\":\"您正在購買商品\"}}");
log.info("驗籤引數集合 {}", JSON.toJSONString(param));
String sign = getSign(param, wechatPlatformApiKey);
param.put("sign", sign);
return getMapToXML(param);
}
private static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if (!children.isEmpty()) {
Iterator it = children.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<" + name + ">");
if (!list.isEmpty()) {
sb.append(getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
/**
* @return java.lang.String
* @Author liushan
* @Description Map轉xml資料
* @Date 2018/11/29 16:20
* @Param [param]
**/
public static String getMapToXML(Map<String, String> param) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
for (Map.Entry<String, String> entry : param.entrySet()) {
sb.append("<" + entry.getKey() + ">");
sb.append(entry.getValue());
sb.append("</" + entry.getKey() + ">");
}
sb.append("</xml>");
return sb.toString();
}
/**
* @return java.lang.String
* @Author liushan
* @Description 微信回撥返回引數
* @Date 2018/11/29 16:18
* @Param [return_code, return_msg]
**/
public static String setXML(HashMap<String,String> return_data) {
String returnCode = return_data.get("return_code");
String returnMsg = return_data.get("return_msg");
return "<xml><return_code><![CDATA[" + returnCode
+ "]]></return_code><return_msg><![CDATA[" + returnMsg
+ "]]></return_msg></xml>";
}
/**
* @return java.lang.String
* @Author liushan
* @Description 獲取返回給微信支付的簽名
* @Date 2020年1月7日 17:24:37
* @Param [weChatResult]
**/
public static String getPaySign(Map<String, String> weChatResult, String tradeType, String wechatPlatformApiKey) {
String sign = "";
Map<String, String> param = new HashMap<>();
if (tradeType.equals(WECHAT_JSAPI)) {
param.put("appId", weChatResult.get("appid"));
param.put("nonceStr", weChatResult.get("nonce_str"));
param.put("timeStamp", weChatResult.get("timestamp"));
param.put("signType", weChatResult.get("sign_type"));
param.put("package", "prepay_id=" + weChatResult.get("prepay_id"));
} else {
param.put("appid", weChatResult.get("appid"));
param.put("partnerid", weChatResult.get("mch_id"));
param.put("prepayid", weChatResult.get("prepay_id"));
param.put("noncestr", weChatResult.get("nonce_str"));
param.put("timestamp", weChatResult.get("timestamp"));
param.put("package", weChatResult.get("packageInfo"));
}
sign = getSign(param, wechatPlatformApiKey);
return sign;
}
/**
* 微信退款服務方法
* @return
*/
public static Map<String, String> sendRefundPayment(RefundInfo refundInfo) {
log.info("微信原路返回入參 {}", JSON.toJSONString(refundInfo));
String refundFee = String.valueOf(refundInfo.getRefundFee().multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP).intValue());
String totalFee = String.valueOf(refundInfo.getTotalFee().multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP).intValue());
String xml = wXParam(refundInfo.getAppid(), refundInfo.getMchId(), refundInfo.getNotifyUrl(), refundFee, totalFee, refundInfo.getPayNumber(),
refundInfo.getOutRefundNo(), refundInfo.getWechatPlatformApiKey(),WECHAT_RETUND);
//微信退款傳送訊息
//載入證照
String res = initCert(refundInfo.getWeChatRefundPrepayUrl(), xml, refundInfo.getMchId());
Map<String, String> data = null;
try {
data = doXMLParse(res);
} catch (Exception e) {
}
return data;
}
/**
* 微信支付查詢結果
*/
public static Map<String, String> queryWeChatPay(String appid, String mchId, String serialNumber, String wechatPlatformApiKey,String weChatQueryPrepayUrl) {
String xml = wXParam(appid, mchId,"","","","",serialNumber,wechatPlatformApiKey,WECHAT_PAY_QUERY);
String res = httpsRequest(weChatQueryPrepayUrl, "POST", xml);
Map<String, String> data = null;
try {
data = doXMLParse(res);
} catch (Exception e) {
}
return data;
}
/**
* 微信退款查詢結果
*/
public static Map<String, String> queryRetundWeChatPay(String appid, String mchId, String serialNumber, String wechatPlatformApiKey,String weChatQueryRefundPrepayUrl) {
String xml = wXParam(appid, mchId, "", "", "","" ,serialNumber,wechatPlatformApiKey,WECHAT_RETUND_QUERY);
String res = httpsRequest(weChatQueryRefundPrepayUrl, "POST", xml);
Map<String, String> data = null;
try {
data = doXMLParse(res);
} catch (Exception e) {
}
return data;
}
// 微信退款/查詢引數設定
private static String wXParam(String appid, String mchId, String refundNotifyUrl, String refundFee,String totalFee,
String payNumber, String outTradeNo, String wechatPlatformApiKey,String type) {
Map<String,String> param = new HashMap<String,String>();
//微信退款
if (WECHAT_RETUND.equals(type)){
param.put("nonce_str", UUID.randomUUID().toString().replace("-", ""));
param.put("appid",appid);
param.put("mch_id",mchId);
param.put("notify_url",refundNotifyUrl);
param.put("refund_fee",refundFee);
param.put("total_fee",totalFee);
param.put("transaction_id",payNumber);
param.put("out_refund_no",outTradeNo);
}//微信付款查詢
else if (WECHAT_PAY_QUERY.equals(type)){
param.put("nonce_str", UUID.randomUUID().toString().replace("-", ""));
param.put("appid",appid);
param.put("mch_id",mchId);
param.put("out_trade_no",outTradeNo);
// param.put("transaction_id","4200000689202009029472000195");
}//微信退款查詢
else if (WECHAT_RETUND_QUERY.equals(type)){
param.put("nonce_str", UUID.randomUUID().toString().replace("-", ""));
param.put("appid",appid);
param.put("mch_id",mchId);
param.put("out_refund_no",outTradeNo);
}
String sign = getSign(param, wechatPlatformApiKey);
param.put("sign", sign);
return getMapToXML(param);
}
// 發起微信支付請求
private static String httpsRequest(String requestUrl, String requestMethod,
String outputStr) {
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
HttpURLConnection conn = null;
try {
URL url = new URL(requestUrl);
conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 設定請求方式(GET/POST)
conn.setRequestMethod(requestMethod);
conn.setRequestProperty("content-type",
"application/x-www-form-urlencoded");
// 當outputStr不為null時向輸出流寫資料
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意編碼格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 從輸入流讀取返回內容
inputStream = conn.getInputStream();
inputStreamReader = new InputStreamReader(inputStream, "utf-8");
bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
return buffer.toString();
} catch (ConnectException ce) {
System.out.println("連線超時:{}" + ce);
} catch (Exception e) {
System.out.println("https請求異常:{}" + e);
}finally {
// 釋放資源
try{
if (Objects.nonNull(bufferedReader)){
bufferedReader.close();
}
if (Objects.nonNull(inputStreamReader)){
inputStreamReader.close();
}
if (Objects.nonNull(inputStream)){
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if (Objects.nonNull(conn)){
conn.disconnect();
}
inputStream = null;
}
return null;
}
// 組裝引數
public static Map<String, String> doXMLParse(String strxml)
throws Exception {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if (null == strxml || "".equals(strxml)) {
return null;
}
Map<String, String> m = new HashMap<String, String>();
InputStream in = null;
try{
in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
//指向根節點
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if (children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = getChildrenText(children);
}
m.put(k, v);
}
}catch (Exception e){
e.printStackTrace();
}finally {
if (Objects.nonNull(in)){
// 關閉流
in.close();
}
}
return m;
}
/**
* @Author liushan
* @Description //TODO 獲取
* @Date 2020/1/3 15:26
* @Param [param, wechatPlatformApiKey]
* @return java.lang.String
**/
public static String getSign(Map<String, String> param, String wechatPlatformApiKey) {
log.info("param {}",JSON.toJSONString(param));
String paramUrl = formatUrlMap(param, true, false);
log.info("StringA {}",paramUrl);
if (!StringUtils.isNotBlank(paramUrl)){
return null;
}
String stringSignTemp = MD5Utils.md5(java.net.URLDecoder.decode(paramUrl) + "&key=" + wechatPlatformApiKey)
.toUpperCase();
return stringSignTemp;
}
/**
* 方法用途: 對所有傳入引數按照欄位名的 ASCII 碼從小到大排序(字典序),並且生成url引數串<br>
* 實現步驟: <br>
*
* @param paraMap 要排序的Map物件
* @param urlEncode 是否需要URLENCODE
* @param keyToLower 是否需要將Key轉換為全小寫 true:key轉化成小寫,false:不轉化
* @return
*/
private static String formatUrlMap(Map<String, String> paraMap,
boolean urlEncode, boolean keyToLower) {
if (paraMap == null) {
return "";
}
String buff = "";
Map<String, String> tmpMap = paraMap;
try {
List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(
tmpMap.entrySet());
// 對所有傳入引數按照欄位名的 ASCII 碼從小到大排序(字典序)
Collections.sort(infoIds,
new Comparator<Map.Entry<String, String>>() {
@Override
public int compare(Map.Entry<String, String> o1,
Map.Entry<String, String> o2) {
return (o1.getKey()).toString().compareTo(
o2.getKey());
}
});
// 構造URL 鍵值對的格式
StringBuilder buf = new StringBuilder();
for (Map.Entry<String, String> item : infoIds) {
String key = item.getKey();
String val = item.getValue();
if (urlEncode) {
val = URLEncoder.encode(val, "utf-8");
}
if (keyToLower) {
buf.append(key.toLowerCase() + "=" + val);
} else {
buf.append(key + "=" + val);
}
buf.append("&");
}
buff = buf.toString();
if (buff.equals("") == false) {
buff = buff.substring(0, buff.length() - 1);
}
} catch (Exception e) {
return null;
}
return buff;
}
//載入證照
private static String initCert(String url,String xml,String mchId){
RestTemplate restTemplate = null;
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12");//eg. PKCS12
ClassPathResource cp = new ClassPathResource("apiclient_cert.p12");
keyStore.load(cp.getInputStream(), mchId.toCharArray());
SSLContext sslcontext = SSLContextBuilder.create()
.loadKeyMaterial(keyStore, mchId.toCharArray())
.build();
// SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, NoopHostnameVerifier.INSTANCE);
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
// HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
// restTemplate = new RestTemplate(factory);
// //將轉換器的編碼換成utf-8
// restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(Charset.forName("utf-8")));
try {
HttpPost httpost = new HttpPost(url); // 設定響應頭資訊
httpost.addHeader("Connection", "keep-alive");
httpost.addHeader("Accept", "*/*");
httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
httpost.addHeader("Host", "api.mch.weixin.qq.com");
httpost.addHeader("X-Requested-With", "XMLHttpRequest");
httpost.addHeader("Cache-Control", "max-age=0");
httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
httpost.setEntity(new StringEntity(xml, "UTF-8"));
CloseableHttpResponse response = httpClient.execute(httpost);
try {
HttpEntity entity = response.getEntity();
String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
EntityUtils.consume(entity);
return jsonStr;
} finally {
response.close();
}
}finally {
httpClient.close();
}
} catch (Exception e) {
throw new BusinessException(BusinessCodeEnum.DATA_IS_NULL.getCode(), "微信退款載入證照失敗,請聯絡管理員!");
}
}
}
MD5Utils
package fly.system.bms.utils;
import java.security.MessageDigest;
public class MD5Utils {
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
resultSb.append(byteToHexString(b[i]));
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n += 256;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname))
resultString = byteArrayToHexString(md.digest(resultString
.getBytes()));
else
resultString = byteArrayToHexString(md.digest(resultString
.getBytes(charsetname)));
} catch (Exception exception) {
}
return resultString;
}
private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
// MD5加密
public final static String md5(String s) {
char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F' };
try {
byte[] btInput = s.getBytes();
// 獲得MD5摘要演算法的 MessageDigest 物件
MessageDigest mdInst = MessageDigest.getInstance("MD5");
// 使用指定的位元組更新摘要
mdInst.update(btInput);
// 獲得密文
byte[] md = mdInst.digest();
// 把密文轉換成十六進位制的字串形式
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
五、微信支付非同步回撥
@Api(tags = {"【微信支付API】"})
@RestController
@RequestMapping("/wechat")
@Slf4j
public class WeCateApiController {
/**
* @return java.lang.String
* @Author hfq
* @Description
* @Date 2020/1/3 15:49
* @Param [request, response]
**/
@PostMapping("/callback")
public String payNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
InputStream inStream = request.getInputStream();
StringBuffer sffer = new StringBuffer();
BufferedReader buffInt = null;
HashMap<String,String> return_data = null;
try{
String str = new String();
return_data = new HashMap<String,String>();
buffInt = new BufferedReader(new InputStreamReader(inStream, "UTF-8"));
while ((str = buffInt.readLine()) != null) {
sffer.append(str);
}
Map<String, String> results = WechatPayUtils.doXMLParse(sffer.toString());
log.info("微信支付回撥引數【" + results + "】");
//微信支付單號
String transactionId = results.get("transaction_id");
//微信訂單號
String outTradeNo = results.get("out_trade_no");
/**
*判斷該筆訂單是否已經處理(去三方流水錶中查詢當前訂單的狀態)
*/
if(!"SUCCESS".equals(results.get("return_code").toString())) {
return_data.put("return_code", "FAIL");
return_data.put("return_msg", "return_code不正確");
}
if (!"SUCCESS".equals(results.get("result_code").toString())) {
return_data.put("return_code", "FAIL");
return_data.put("return_msg", "result_code不正確");
}
//判斷金額是否正確
long total_amount = new BigDecimal(results.get("total_fee")).longValue();
if ( total_amount != tripartitePay.getAmount().multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP).longValue()){
return_data.put("return_code", "FAIL");
return_data.put("return_msg", "返回金額不正確");
throw new BusinessException(BusinessCodeEnum.RETURN_ERROR.getCode(), "金額total_amount錯誤");
}
/**
* 第一步:首先通過resultCode判斷付款成功還是失敗
* 第二步:如果付款失敗:
* 1、修改三方流水錶
*/
PayCallBackParam payCallBackParam = PayCallBackParam.builder()
.outTradeNo(outTradeNo)
.payNumber(transactionId) .timeEnd(results.get("time_end")) .totalFee(results.get("total_fee")) .returnMsg(results.get("return_msg")) .resultCode(results.get("result_code")) .errCode(results.get("err_code")) .errCodeDesc(results.get("err_code_des")) .payRetundType(PayRefundTypeEnum.PAY_MODE.getCode().toString()) .transactionId(tripartitePay.getTransactionId()) .tripartitePayInfoId(tripartitePay.getId()) .transactionCode(tripartitePay.getTransactionCode())
.result(sffer.toString())
.build();
//處理業務
return_data.put("return_code","SUCCESS");
return_data.put("return_msg","OK");
return WechatPayUtils.setXML(return_data);
}catch(BusinessException e){
if (Objects.nonNull(return_data)){
return_data.put("return_code", "FAIL");
return_data.put("return_msg", "執行時異常");
}
throw new BusinessException(BusinessCodeEnum.SYSTEM_ERROR.getCode(),e.getMessage());
}finally {
if(Objects.nonNull(buffInt)){
buffInt.close();
}
if(Objects.nonNull(inStream)){
inStream.close();
}
}
}
}
可以通過查詢result_code的值判斷是否支付成功
相關文章
- 微信H5支付如何呼叫外部瀏覽器完成支付H5瀏覽器
- 微信支付之手機H5支付實踐H5
- iOS WKWebView H5微信支付跳轉iOSWebViewH5
- 微信內建H5支付H5
- 前端呼叫微信小程式的支付流程前端微信小程式
- Vue H5微信瀏覽器外進行支付 H5支付VueH5瀏覽器
- 快應用微信H5支付H5
- 解答!大象跳轉是如何實現微信H5支付呼叫外部瀏覽器完成支付的H5瀏覽器
- 關於建行龍支付的聚合支付微信,支付寶 對接PC和H5H5
- 微信支付,支付寶支付
- 微信H5支付 驗籤失敗 問題H5
- 支付寶、微信支付(.NET)
- 關於微信支付,支付寶支付
- 微信H5支付(手機瀏覽器請求)H5瀏覽器
- 微信H5支付 - Referer丟失問題總結H5
- 微信JSAPI支付JSAPI
- 微信App支付APP
- 支付寶微信合單支付
- 利用抖音Cookie充值介面提取支付連結,個人調起原生微信h5支付寶h5支付CookieH5
- nodejs微信支付之掃碼支付NodeJS
- 微信、支付寶支付那點事
- PHP-Laravel支付寶支付和微信支付PHPLaravel
- 微信開發超市全反系統,微信支付刷卡支付,微信介面簡單配置!
- 解決iOS微信H5支付跳轉微信後不返回App問題(Swift-WKWebview)iOSH5APPSwiftWebView
- 微信公眾號h5支付 以及獲取code(前端部分)H5前端
- uniapp h5微信分享APPH5
- 微信支付團隊釋出“微信青蛙pro” 支援刷臉支付功能
- Android 微信支付 微信是否安裝判斷Android
- 微信小程式之支付微信小程式
- PHP微信支付開發PHP
- 微信jsapi支付 退款介面JSAPI
- uni-app 微信支付APP
- 移動H5頁面微信支付踩坑之旅(微信支付、單頁面路由模擬、按鈕加鎖、輪詢等常見功能)H5路由
- 關於支付寶以及微信支付的整合
- Laravel 搞定支付寶和微信掃碼支付Laravel
- JAVA版微信支付V3—JSAPI支付JavaJSAPI
- android微信分享、微信支付的一些坑Android
- 微信App支付:微信支付的appid,appsecret,商戶號mch_id,微信交易支付金鑰(mch_key)在哪裡檢視APP