app微信支付
pom依賴
<!--微信支付-->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
<dependency>
<groupId>org.jodd</groupId>
<artifactId>jodd-core</artifactId>
<version>5.1.5</version>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.2.1</version>
</dependency>
yml中的相關的配置

實作類里的具體內容,(一般流程是,要呼叫預下單介面,然后回傳給安卓的二次簽名,安卓拿著二次簽名去拉起微信支付,如果成功拉起支付,就可以付款,付款成功以后,就會主動回呼咱們自己寫的回呼介面,修改業務邏輯,最后完成此次支付)
預下單介面
@Value("${ayj.wechat.notifyUrlMall}")
private String notifyUrlMall;
@Value("${ayj.wechat.notifyUrlMiniMall}")
private String notifyUrlMiniMall;
@Value("${ayj.wechat.appId}")
private String appId;
@Value("${ayj.wechat.miniAppId}")
private String miniAppId;
@Value("${ayj.wechat.mchId}")
private String mchId;
@Value("${ayj.wechat.bodyMall}")
private String bodyMall;
@Value("${ayj.wechat.bodyCharity}")
private String bodyCharity;
@Value("${ayj.wechat.key}")
private String key;
@Value("${ayj.wechat.feeType}")
private String feeType;
@Value("${ayj.wechat.tradeType}")
private String tradeType;
@Value("${ayj.wechat.miniTradeType}")
private String miniTradeType;
@Value("${ayj.wechat.package}")
private String wxPackage;
/**
* 預下單介面--商城-app
*
* @return
*/
@Override
public R<WXPaymentResponseDto> prePayMall(Integer userId, Integer orderId,BigDecimal money) {
int penny = money.multiply(new BigDecimal("100")).intValue();//已分為單位
Map<String, String> resp = new HashMap<>();
try {
WXPay wxpay = appConfigService.myWXPay();
SortedMap<String, String> data = new TreeMap<String, String>();
data.put("appid", appId);//微信支付id
data.put("mch_id", mchId);//商戶號
data.put("nonce_str", WXPayUtil.generateNonceStr());
data.put("sign_type", "MD5");
data.put("body", bodyMall);
data.put("out_trade_no", OrderNoUtil.getOrderId());//訂單號
data.put("fee_type", feeType);// 金額型別 默認CNY
data.put("total_fee", penny+"");//訂單處理金額
data.put("notify_url", notifyUrlMall);//支付成功以后呼叫的地址
data.put("trade_type", tradeType); // 此處指定為app支付
// data.put("profit_sharing","Y");//分賬
//生成簽名--一次簽名,在預下單之前
String characterEncoding = "UTF-8";
String oneSign= createSign(characterEncoding,data,key);
data.put("sign",oneSign);
resp = wxpay.unifiedOrder(data);
log.info("微信預下單回傳的物件:", resp);
if (resp.get("return_code").equals("SUCCESS")) {
log.info("微信預下單成功!");
//生成微信預下單物件
WXPrePay prePay = new WXPrePay();
prePay.setMemberId(userId);//用戶id
prePay.setOrderNo(data.get("out_trade_no"));//支付訂單單號
prePay.setOrderId(orderId);//專案自增id
prePay.setOrderType(MallEmums.PRE_PAY_TYPE_MALL.getCode());//訂單型別
prePay.setTotalAmount(penny);//金額
baseMapper.insert(prePay);
//回傳WXPaymentResponseDto物件給前端
WXPaymentResponseDto dto = new WXPaymentResponseDto();
dto.setNonceStr(resp.get("nonce_str"));
dto.setPrepayId(resp.get("prepay_id"));
dto.setPartnerId(mchId); //商戶id
dto.setAppId(appId); //商戶id
dto.setWxPackage(wxPackage);
String time = new Date().getTime()+"";
dto.setTimestamp(time.substring(0,10));
//統一下單以后,生成的二次簽名
Map<String, String> signmap = new TreeMap<>();
signmap.put("appid",appId);
signmap.put("noncestr",resp.get("nonce_str"));
signmap.put("package",wxPackage);
signmap.put("partnerid",mchId);
signmap.put("prepayid",resp.get("prepay_id"));
// signmap.put("signtype","MD5");
signmap.put("timestamp",time.substring(0,10));//官網上寫到,時間戳要十位并且精確到秒,平常使用的時間戳都是毫秒13位
String sign = WxUtils.createSign(characterEncoding,signmap,key);
System.out.println("二次簽名:"+sign);
dto.setSign(sign.substring(0,30)); //注意,就是這里,為什么是截取簽名的前30位,正常生成的簽名是32位,這是個坑,我被困擾很久,一會附上圖片,告訴大家
return R.success(dto);
}
log.info("微信預下單失敗:",resp.get("return_msg"));
return R.fail("微信預下單失敗:"+resp.get("return_msg"));
} catch (Exception e) {
e.printStackTrace();
return R.fail("微信預下單失敗:"+resp.get("return_msg"));
}
}
在這里給大家附上圖片,我被這個二次簽名困擾48小時,每次安卓一拉起微信支付,就報錯,說是簽名錯誤,我就百度,看到有個大神說,截取前30位就好了,

安卓拉起微信支付以后,并支付成功,這個時候,微信會去自動呼叫咱們的寫的回呼介面
public String callBackMall(HttpServletRequest request, HttpServletResponse response) {
response.setHeader("Content-Type", "application/xml"); //設定回應資料格式為xml
Map<String, String> returnMap = new HashMap<>();
returnMap.put("return_code", "SUCCESS");
returnMap.put("return_msg", "");
Long hxcode =null;
// 讀取引數,決議Xml為map
Map<String, String> map = null;
try {
map = WXPayUtil.xmlToMap(wxUtils.readRequest(request));
// 轉換為有序 map,判斷簽名是否正確
boolean isSignSuccess = WXPayUtil.isSignatureValid(new TreeMap<String,
String>(map), key, WXPayConstants.SignType.HMACSHA256);
// 簽名校驗成功,說明是微信服務器發出的資料
if (isSignSuccess) {
//拿出訂單支付編號
String orderNo = map.get("out_trade_no");
//根據訂單支付編號去查找訂單id,等相關的資料
WXPrePay pay = prePayService.getWxPrePayByOrderNo(orderNo).getData();
System.out.println("預下單物件:" + pay);
Integer orderId = pay.getOrderId();
String transactionId = map.get("transaction_id");
System.out.println("微信訂單支付單號" + transactionId);
Boolean b = orderFeignService.hasPayByOrderId(orderId).getData();
System.out.println(b);
if (!b) { //false,已支付------一定要在這里去判斷是否支付成功,因為微信支付的回呼,是分時間,一直在呼叫,這個具體看官網怎么寫的
returnMap.put("hx_code", hxcode.toString());
return WXPayUtil.mapToXml(returnMap);
}
if (map.get("return_code").equals("SUCCESS")) {
if (map.get("result_code").equals("SUCCESS")) {
System.out.println("微信回呼:支付成功,orderNo為:" + orderNo);
//------------------自己處理業務邏輯
pay.setTransactionId(transactionId);
prePayService.updateById(pay);
System.out.println(map);
returnMap.put("hx_code", hxcode.toString());
return WXPayUtil.mapToXml(returnMap);
} else {
System.out.println("微信回呼:支付失敗,orderNo為:" + orderNo);
}
}
// 簽名校驗失敗(可能不是微信服務器發出的資料)
returnMap.put("return_code", "FAIL");
returnMap.put("return_msg", "");
return WXPayUtil.mapToXml(returnMap);
} else {
// 簽名校驗失敗(可能不是微信服務器發出的資料)
returnMap.put("return_code", "FAIL");
returnMap.put("return_msg", "");
return WXPayUtil.mapToXml(returnMap);
}
} catch (IOException e) {
e.printStackTrace();
return "<xml>\n" +
" <return_code><![CDATA[FAIL]]></return_code>\n" +
" <return_msg><![CDATA[]]></return_msg>\n" +
"</xml>";
} catch (Exception e) {
e.printStackTrace();
return "<xml>\n" +
" <return_code><![CDATA[FAIL]]></return_code>\n" +
" <return_msg><![CDATA[]]></return_msg>\n" +
"</xml>";
}
}
這里附上兩個工具類,上面會用到
package com.ayjmall.thirdparty.wechat.utils;
import jodd.util.ResourcesUtil;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
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.SSLContexts;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
/**
* @Auther swj
* @description 帶雙向證書的post請求工具類
* @Date 2021/1/20
*/
public class WxCertHttpUtil {
private static int socketTimeout = 10000;// 連接超時時間,默認10秒
private static int connectTimeout = 30000;// 傳輸超時時間,默認30秒
private static RequestConfig requestConfig;// 請求配置
private static CloseableHttpClient httpClient;// HTTP請求
/**
*
* @param url API地址
* @param --xmlObj 要提交的XML
* @param mchId 服務商商戶ID
* @param --certPath證書路徑
* @return
*/
public static String postData(String url, String xml, String mchId, String certPath) {
// 加載證書
try {
loadCert(mchId, certPath);
} catch (Exception e) {
e.printStackTrace();
}
String result = null;
HttpPost httpPost = new HttpPost(url);
StringEntity postEntity = new StringEntity(xml, "UTF-8");
httpPost.addHeader("Content-Type", "application/xml");
httpPost.setEntity(postEntity);
requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
httpPost.setConfig(requestConfig);
try {
HttpResponse response = null;
try {
response = httpClient.execute(httpPost);
} catch (IOException e) {
e.printStackTrace();
}
HttpEntity entity = response.getEntity();
try {
result = EntityUtils.toString(entity, "UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
} finally {
httpPost.abort();
}
return result;
}
/**
*加載證書
*
* @param mchId 服務商商戶ID
* @param certPath 證書路徑
* @throws Exception
*/
private static void loadCert(String mchId, String certPath) throws Exception {
// 證書密碼,默認為服務商商戶ID
String key = mchId;
// 證書路徑
String path = certPath;
if (!path.startsWith("/")) {
path = "/" + path;
}
// 指定證書格式為PKCS12
KeyStore keyStore = KeyStore.getInstance("PKCS12");
// 讀取PKCS12證書檔案
InputStream instream = ResourcesUtil.getResourceAsStream(path);
try {
// 指定PKCS12的密碼(商戶ID)
keyStore.load(instream, key.toCharArray());
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build();
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslcontext, NoopHostnameVerifier.INSTANCE);
httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
}
}
package com.ayjmall.thirdparty.wechat.utils;
import com.ayjmall.common.utils.MD5Util;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
/**
* Created by suneo.
* User: neo
* Date: 29/01/2018
* Time: 5:21 PM
* Describe:
* <編碼格式統一為 UTF-8>
* <p>
* 便于使用,將所有的工具方法都集中在此,包含:
* 1. 執行 HTTP POST 請求,回傳執行結果的 String
* 2. 創建簽名(為下單資料創建)
* 3. 創建簽名(為 APP 創建)
* 4. 檢驗簽名
* 5. 讀取 HTTP Request 內容
* 6. 讀取 HTTP Response 內容
* 7. 將 Map 轉化為 Xml
* 8. 將 Xml 轉化為 Map
* 9. 生成 32 位隨機字串
* 10. MD5 簽名
*/
@Service
public class WxUtils {
/**
* 微信支付簽名演算法sign
*
* @param characterEncoding
* @param parameters
* @return
*/
public static String createSign(String characterEncoding, Map<String, String> parameters, String key) {
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();// 所有參與傳參的引數按照accsii排序(升序)
Iterator it = es.iterator();
while (it.hasNext()) {
@SuppressWarnings("rawtypes")
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
Object v = entry.getValue();
if (null != v && !"".equals(v) && !"sign".equals(k)
&& !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + key); //KEY是商戶秘鑰
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding)
.toUpperCase();
return sign;
}
/**
* 讀取 request body 內容作為字串
*
* @param request
* @return
* @throws IOException
*/
public String readRequest(HttpServletRequest request) throws IOException {
InputStream inputStream;
StringBuffer sb = new StringBuffer();
inputStream = request.getInputStream();
String str;
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while ((str = in.readLine()) != null) {
sb.append(str);
}
in.close();
inputStream.close();
return sb.toString();
}
}
小程式支付
小程式支付跟app支付有不一樣的地方,但是區別不大,我已經在代碼中注釋了,小程式有單獨的小程式id.還要在單獨指定小程式的支付方式,而且還有單獨的小程式的openid,請大家注意
public R<WXPaymentResponseDto> MiniPrePayMall(Integer userId, Integer orderId, BigDecimal money) {
AyjMallMembers members = memberFeignService.getMembersById(userId).getData();
int penny = money.multiply(new BigDecimal("100")).intValue();//已分為單位
Map<String, String> resp = new HashMap<>();
try {
MiniWXPay miniWXPay = miniUserMyConfig.miniMyWXPay();
SortedMap<String, String> data = new TreeMap<String, String>();
data.put("appid", miniAppId);//微信小程式支付id
data.put("mch_id", mchId);//商戶號
data.put("nonce_str", MiniWXPayUtil.generateNonceStr());
data.put("sign_type", "MD5");
data.put("body", bodyMall);
data.put("out_trade_no", OrderNoUtil.getOrderId());//訂單號
data.put("fee_type", feeType);// 金額型別 默認CNY
data.put("total_fee", penny+"");//訂單處理金額
data.put("notify_url", notifyUrlMiniMall);//支付成功以后呼叫的地址
data.put("trade_type", miniTradeType); // 此處指定為小程式支付
data.put("openid",members.getOpenidMiniUserId());//小程式支付必須使用小程式登錄的openid
//生成簽名--一次簽名,在預下單之前
String characterEncoding = "UTF-8";
String oneSign= createSign(characterEncoding,data, key);
data.put("sign",oneSign);
resp = miniWXPay.unifiedOrder(data);
log.info("微信預下單回傳的物件-----------------:", resp);
if (resp.get("return_code").equals("SUCCESS")) {
log.info("微信預下單成功!");
//生成微信預下單物件
WXPrePay prePay = new WXPrePay();
prePay.setMemberId(userId);//用戶id
prePay.setOrderNo(data.get("out_trade_no"));//支付訂單單號
prePay.setOrderId(orderId);//專案自增id
prePay.setOrderType(4);//訂單型別
prePay.setTotalAmount(penny);//金額
baseMapper.insert(prePay);
//回傳WXPaymentResponseDto物件給前端
WXPaymentResponseDto dto = new WXPaymentResponseDto();
dto.setNonceStr(resp.get("nonce_str"));
dto.setPrepayId(resp.get("prepay_id"));
dto.setPartnerId(mchId); //商戶id
dto.setAppId(miniAppId); //商戶id
dto.setWxPackage(wxPackage);
String time = new Date().getTime()+"";
dto.setTimestamp(time.substring(0,10));
//統一下單以后,生成的二次簽名
Map<String, String> signmap = new TreeMap<>();
signmap.put("appId",miniAppId);
signmap.put("nonceStr",resp.get("nonce_str"));
signmap.put("package","prepay_id="+resp.get("prepay_id"));
signmap.put("signType","MD5");
signmap.put("timeStamp",time.substring(0,10));
//signmap.put("mchId",mchId);
String sign = WxUtils.createSign(characterEncoding,signmap,key);//商戶的key
System.out.println(signmap+"---------------");
System.out.println("二次簽名:"+sign);
dto.setSign(sign);
System.out.println(resp);
return R.success(dto);
}
log.info("微信預下單失敗:",resp.get("return_msg"));
return R.fail("微信預下單失敗:"+resp.get("return_msg"));
} catch (Exception e) {
e.printStackTrace();
return R.fail("微信預下單失敗:"+resp.get("return_msg"));
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/340735.html
標籤:java
