微信支付檔案傳送門:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3
1.開發工具:
idea+springcloud+微信web開發工具
2.開發環境:
java+maven
3.開發前準備:
3.1 所需材料
小程式的appid,APPsecret,支付商戶號(mch_id),商戶密鑰(key),付款用戶的openid,
申請接入微信商戶地址:https://pay.weixin.qq.com/static/applyment_guide/applyment_detail_miniapp.shtml
3.2 開發模式
本次開發采用的開發模式是:普通模式,適用于有自己開發團隊或外包開發商的直連商戶收款,開發者申請自己的appid和mch_id,兩者需具備系結關系,以此來使用微信支付提供的開放介面,對用戶提供服務,
開發 java
一、控制層 PaymentController.java
/**
* 微信預支付Api
* 統一下單介面
* @param openId
* @return
*/
@ApiOperation("微信預支付1")
@ApiImplicitParams({
@ApiImplicitParam(name = "openId",value = "用戶openId")
})
@PostMapping(value = {"jsapipays"})
public ResponseEntity jsapipays(@RequestParam String openId) {
Condition usercondition = new Condition(User.class);
usercondition.createCriteria().andEqualTo("wxId",openId);
User user=userService.findByCondition(usercondition,null).get(0);
//通過用戶ID查詢藍牙訂單
Condition bizBluetoothOrderC=new Condition(BizBluetoothOrder.class);
bizBluetoothOrderC.createCriteria().andEqualTo("uId",user.getId());
List<BizBluetoothOrder> bizBluetoothOrderlist = bizBluetoothOrderService.findByCondition(bizBluetoothOrderC,null);
if(bizBluetoothOrderlist.isEmpty()){
return failResult("訂單不存在!");
}
BizBluetoothOrder bizBluetoothOrder = bizBluetoothOrderlist.get(0);
Map jsapi = wxPayService.jsapi2(bizBluetoothOrder.getOrderSn(),"18.0" ,openId);
return successResult(jsapi);
}
二、service層
/**
* 微信小程式支付
* @param orderNum
* @param openId
* @return
*/
Map jsapi2(String orderNum , String total_fee, String openId);
三、實作類
/**
* 統一下單開始 方法1
* @param orderNum
* @param total_fee
* @param openId
* @return
*/
@Override
public Map jsapi2(String orderNum , String total_fee, String openId) {
try {
log.info("訂單號=="+orderNum);
//拼接統一下單地址引數
SortedMap<String,String> paraMap = new TreeMap();
paraMap.put("appid", WXConstEnum.appId);
paraMap.put("body", "支付訂單");
paraMap.put("mch_id", WXConstEnum.mch_id);
paraMap.put("nonce_str", WxPayUtils.makeUUID(32).toUpperCase());
paraMap.put("signType",WXConstEnum.SIGNTYPE);
paraMap.put("openid", openId);
paraMap.put("out_trade_no",orderNum);//訂單號 商戶系統內部訂單號,要求32個字符內,只能是數字、大小寫字母_-|*且在同一個商戶號下唯一
paraMap.put("spbill_create_ip", WxPayUtils.getLocalIp());
paraMap.put("total_fee", total_fee);
paraMap.put("timeStamp", WxPayUtils.getCurrentTimeStamp());
paraMap.put("trade_type",WXConstEnum.TRADETYPE);
paraMap.put("notify_url",WXConstEnum.notify_url);// 此路徑是微信服務器呼叫支付結果通知路徑隨意寫
String sign = WXPayUtil.generateSignature(paraMap, WXConstEnum.key).toUpperCase();
paraMap.put("sign", sign);
String xml = WxPayUtils.mapToXml(paraMap);//將所有引數(map)轉xml格式
log.info("xml源串 = " + xml);
// 統一下單地址 https://api.mch.weixin.qq.com/pay/unifiedorder
String xmlStr = HttpUtils.sendPost(WXConstEnum.pay_url, xml);//發送post請求"統一下單介面"回傳預支付id:prepay_id
log.info("回傳xmlStr = " + xmlStr);
//以下內容是回傳前端頁面的json資料
String prepay_id = "";//預支付id
if (xmlStr.indexOf("SUCCESS") != -1) {
Map<String, String> map = WxPayUtils.xmlToMap(xmlStr);
prepay_id = map.get("prepay_id");
}
Map<String, String> payMap = new HashMap<String, String>();
payMap.put("appId", WXConstEnum.appId);
payMap.put("timeStamp", WxPayUtils.getCurrentTimeStamp());
payMap.put("nonceStr", WxPayUtils.makeUUID(32));
payMap.put("signType", "MD5");
String paySign = WXPayUtil.generateSignature(paraMap, WXConstEnum.key).toUpperCase();
payMap.put("paySign", paySign);
payMap.put("package", "prepay_id=" + prepay_id);
return payMap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}四、呼叫統一下單的介面 引數類WXConstEnum.java
/**
* TODO 替換成自己的引數
*/
//微信小程式appid
public static String appId = "wx34118d54080e5555";
//微信商戶號
public static String mch_id="1612911113";
//微信支付的商戶密鑰
public static final String key = "50c01580a2824d48a48d94e9f6b046fb";
//支付成功后的服務器回呼url
public static final String notify_url="http://shopdev.lyproduct.cn/lyshop-app/app/orderapi/miniNotify";
//簽名方式
public static final String SIGNTYPE = "MD5";
//交易型別
public static final String TRADETYPE = "JSAPI";
//微信統一下單介面地址
public static final String pay_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//微信支付回傳狀態碼
public static final String SUCCESS = "SUCCESS";
五、WxPayUtils.java 需要用到的工具類 官方檔案會有提供SDK
/**
* XML格式字串轉換為Map
*
* @param strXML XML字串
* @return XML資料轉換后的Map
* @throws Exception
*/
public static Map<String, String> xmlToMap(String strXML) throws Exception {
try {
Map<String, String> data = new HashMap<String, String>();
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
org.w3c.dom.Document doc = documentBuilder.parse(stream);
doc.getDocumentElement().normalize();
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int idx = 0; idx < nodeList.getLength(); ++idx) {
Node node = nodeList.item(idx);
if (node.getNodeType() == Node.ELEMENT_NODE) {
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
data.put(element.getNodeName(), element.getTextContent());
}
}
try {
stream.close();
} catch (Exception ex) {
// do nothing
}
return data;
} catch (Exception ex) {
throw ex;
}
}
/**
* 將Map轉換為XML格式的字串
*
* @param data Map型別資料
* @return XML格式的字串
* @throws Exception
*/
public static String mapToXml(SortedMap<String, String> data) throws Exception {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
//防止XXE攻擊
documentBuilderFactory.setXIncludeAware(false);
documentBuilderFactory.setExpandEntityReferences(false);
DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
org.w3c.dom.Document document = documentBuilder.newDocument();
org.w3c.dom.Element root = document.createElement("xml");
document.appendChild(root);
for (String key: data.keySet()) {
String value = data.get(key);
if (value == null) {
value = "";
}
value = value.trim();
org.w3c.dom.Element filed = document.createElement(key);
filed.appendChild(document.createTextNode(value));
root.appendChild(filed);
}
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
DOMSource source = new DOMSource(document);
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
transformer.transform(source, result);
String output = writer.getBuffer().toString();
try {
writer.close();
}
catch (Exception ex) {
}
return output;
}
/**
* 生成簽名
*
* @param data 待簽名資料
* @param key API密鑰
* @return 簽名
*/
public static String generateSignature(final Map<String, String> data, String key) throws Exception {
return generateSignature(data, key, WXPayConstants.SignType.MD5);
}
/**
* 生成簽名. 注意,若含有sign_type欄位,必須和signType引數保持一致,
*
* @param data 待簽名資料
* @param key API密鑰
* @param signType 簽名方式
* @return 簽名
*/
public static String generateSignature(final Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals(WXPayConstants.FIELD_SIGN)) {
continue;
}
if (data.get(k).trim().length() > 0) // 引數值為空,則不參與簽名
sb.append(k).append("=").append(data.get(k).trim()).append("&");
}
sb.append("key=").append(key);
if (WXPayConstants.SignType.MD5.equals(signType)) {
return MD5(sb.toString()).toUpperCase();
}
else if (WXPayConstants.SignType.HMACSHA256.equals(signType)) {
return HMACSHA256(sb.toString(), key);
}
else {
throw new Exception(String.format("Invalid sign_type: %s", signType));
}
}
/**
* 生成 MD5
*
* @param data 待處理資料
* @return MD5結果
*/
public static String MD5(String data) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 生成 HMACSHA256
* @param data 待處理資料
* @param key 密鑰
* @return 加密結果
* @throws Exception
*/
public static String HMACSHA256(String data, String key) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 獲取有序map
* @param map
* @return
*/
public static SortedMap<String,String> getSortedMap(Map<String,String> map){
SortedMap<String, String> sortedMap = new TreeMap<>();
Iterator<String> it = map.keySet().iterator();
while (it.hasNext()){
String key = (String)it.next();
String value = map.get(key);
String temp = "";
if( null != value){
temp = value.trim();
}
sortedMap.put(key,temp);
}
return sortedMap;
}
/**
* 生成亂數
*
* @return
*/
public static String makeUUID(int len) {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, len);
}
/**
* 獲取當前的Timestamp
*
* @return
*/
public static String getCurrentTimeStamp() {
return Long.toString(System.currentTimeMillis()/1000);
}
/**
* 獲取當前的時間
* @return
*/
public static long getCurrentTimestampMs() {
return System.currentTimeMillis();
}
/**
* 獲取當前機器的ip
*
* @return String
*/
public static String getLocalIp(){
InetAddress ia=null;
String localip = null;
try {
ia=ia.getLocalHost();
localip=ia.getHostAddress();
} catch (Exception e) {
e.printStackTrace();
}
return localip;
}
/**
* 轉換金額型到整型
* @param money
* @return
*/
public static String moneyToIntegerStr(Double money){
BigDecimal decimal = new BigDecimal(money);
int amount = decimal.multiply(new BigDecimal(100))
.setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
return String.valueOf(amount);
}
/**
* 生成訂單號
*
* @return
*/
public static String generateOrderNo() {
SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");
return sdf.format(new Date())+makeUUID(16);
}
/**
* 獲取當前工程url
*
* @param request
* @return
*/
public static String getCurrentUrl(HttpServletRequest request){
return request.getScheme() +"://" + request.getServerName() + ":" +request.getServerPort() +request.getContextPath();
}
/**
* 轉換double 為 int string
* @param payAmount
* @return
*/
public static String moneyToIntegerStr(double payAmount){
String money = String.valueOf(payAmount);
return money;
}
傳遞的xml引數必須按照官方介面檔案(https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1),如果出現sing簽名錯誤的問題,試試修改商戶密鑰(本人修改了三次才成功),
control層呼叫介面時 我是值傳遞了openId 可以根據下單的需要去其他引數(注意大小寫)
所有的引數都有,應該不會看不懂吧,直接復制粘貼!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/295386.html
標籤:其他
上一篇:發送短信打開帶引數小程式
下一篇:自動化測驗相關知識 ~第一更~
