說實話 對於初學者來講支付來講確實要花很大的精力去看文檔 java
提早說明一下 支付寶和微信手續費 0.6% 按每比計算 退款想要原金額退回 0.6%由商家承擔算法
這裏我只分享後端支付這一塊,好了直接上代碼 數據庫
APPID=**** PARTNER=**** APP_PUBLIC_KEY=*** APP_PRIVATE_KEY=*** APP_PRODUCT_CODE=QUICK_MSECURITY_PAY APP_METHOD=alipay.trade.app.pay APP_FORMAT=json APP_CHARSET=utf-8
#編碼格式 APP_SIGN_TYPE=RSA2
#版本號 APP_VERSION=1.0
#異步通知 APP_NOTIFY_URL=http:
#輔助api統一請求 APP_ALIPAY_URL=https://openapi.alipay.com/gateway.do
這個是支付寶配置文件express
支付寶工具類json
package com.yuanxinxinxi.yuanxinbuluo.alipay.util; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.spec.PKCS8EncodedKeySpec; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import com.alipay.api.AlipayApiException; import com.alipay.api.internal.util.AlipaySignature; import com.yuanxinxinxi.yuanxinbuluo.alipay.dto.AlipayConfig; /** * 支付寶工具類*/ public class AlipayUtil { private static final String ALGORITHM = "RSA"; private static final String SIGN_SHA256RSA_ALGORITHMS = "SHA256WithRSA"; private static final String DEFAULT_CHARSET = "UTF-8"; /** * 支付寶消息驗證地址 */ private static final String HTTPS_VERIFY_URL = "https://mapi.alipay.com/gateway.do?service=notify_verify&"; /** * 獲取遠程服務器ATN結果,驗證返回URL * @param notify_id 通知校驗ID * @return 服務器ATN結果 * 驗證結果集: * invalid命令參數不對 出現這個錯誤,請檢測返回處理中partner和key是否爲空 * true 返回正確信息 * false 請檢查防火牆或者是服務器阻止端口問題以及驗證時間是否超過一分鐘 */ public static String verifyResponse(String notify_id) { //獲取遠程服務器ATN結果,驗證是不是支付寶服務器發來的請求 String partner = AlipayConfig.PARTNER; String veryfy_url = HTTPS_VERIFY_URL + "partner=" + partner + "¬ify_id=" + notify_id; return checkUrl(veryfy_url); } /** * 獲取遠程服務器ATN結果 * @param urlvalue 指定URL路徑地址 * @return 服務器ATN結果 * 驗證結果集: * invalid命令參數不對 出現這個錯誤,請檢測返回處理中partner和key是否爲空 * true 返回正確信息 * false 請檢查防火牆或者是服務器阻止端口問題以及驗證時間是否超過一分鐘 */ public static String checkUrl(String urlvalue) { String inputLine = ""; try { URL url = new URL(urlvalue); HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection .getInputStream())); inputLine = in.readLine().toString(); } catch (Exception e) { e.printStackTrace(); inputLine = ""; } return inputLine; } /** * 根據時間轉換成yyyy-MM-dd HH:mm:ss格式的時間 * @param time * @return */ public static String getDateTime(Date time){ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(time); } /** * 對支付參數信息進行簽名 * * @param map * 待簽名受權信息 * * @return */ public static String getSign(Map<String, Object> map, String rsaKey) { List<String> keys = new ArrayList<String>(map.keySet()); // key排序 Collections.sort(keys); StringBuilder authInfo = new StringBuilder(); for (int i = 0; i < keys.size() - 1; i++) { String key = keys.get(i); String value = String.valueOf(map.get(key)); authInfo.append(buildKeyValue(key, value, false)); authInfo.append("&"); } String tailKey = keys.get(keys.size() - 1); String tailValue = String.valueOf(map.get(tailKey)); authInfo.append(buildKeyValue(tailKey, tailValue, false)); String oriSign = sign(authInfo.toString(), rsaKey); String encodedSign = ""; try { encodedSign = URLEncoder.encode(oriSign, DEFAULT_CHARSET); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return "sign="+encodedSign; } /** * 對支付寶參數編碼 * @param * @return */ public static String getReqestMap(Map<String,Object> map){ List<String> keys = new ArrayList<String>(map.keySet()); Collections.sort(keys); StringBuffer buffer = new StringBuffer(); for (int i = 0; i < keys.size(); i++) { String key = keys.get(i); String value = String.valueOf(map.get(key)); buffer.append(buildKeyValue(key, value, true)); buffer.append("&"); } return buffer.toString(); } /** * 拼接鍵值對 * * @param key * @param value * @param isEncode * @return */ private static String buildKeyValue(String key, String value, boolean isEncode) { StringBuilder sb = new StringBuilder(); sb.append(key); sb.append("="); if (isEncode) { try { sb.append(URLEncoder.encode(value, DEFAULT_CHARSET)); } catch (UnsupportedEncodingException e) { sb.append(value); } } else { sb.append(value); } return sb.toString(); } /** * 對支付參數信息進行簽名配合getSign方法 */ public static String sign(String content, String privateKey) { try { PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec( Base64.decode(privateKey)); KeyFactory keyf = KeyFactory.getInstance(ALGORITHM); PrivateKey priKey = keyf.generatePrivate(priPKCS8); java.security.Signature signature = java.security.Signature .getInstance(SIGN_SHA256RSA_ALGORITHMS); signature.initSign(priKey); signature.update(content.getBytes(DEFAULT_CHARSET)); byte[] signed = signature.sign(); return Base64.encode(signed); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 把空值去掉 * @param map * @return */ public static Map<String,Object> isVlaueNull(Map<String,Object> map){ Iterator<Map.Entry<String,Object>> iterator = map.entrySet().iterator(); while(iterator.hasNext()) { Map.Entry<String,Object> entry = iterator.next(); String key = entry.getKey(); if(map.get(key)==null){ iterator.remove(); } } return map; } /** * 驗證消息是不是支付寶發出的合法消息 * * @param params * 通知返回來的參數數組 * @return 驗證結果 * @throws AlipayApiException */ public static boolean verify(Map<String, String> params) throws AlipayApiException { // 判斷responsetTxt是否爲true,isSign是否爲true // responsetTxt的結果不是true,與服務器設置問題、合做身份者ID、notify_id一分鐘失效有關 // isSign不是true,與安全校驗碼、請求時的參數格式(如:帶自定義參數等)、編碼格式有關 String responseTxt = "false"; if (params.get("notify_id") != null) { String notify_id = params.get("notify_id"); responseTxt = verifyResponse(notify_id); } String sign = ""; if (params.get("sign") != null) { sign = params.get("sign"); } boolean isSign = getSignVeryfy(params, sign); if (isSign && responseTxt.equals("true")) { return true; } else { return false; } } /** * 根據反饋回來的信息,生成簽名結果 * * @param Params * 通知返回來的參數數組 * @param sign * 比對的簽名結果 * @return 生成的簽名結果 * @throws AlipayApiException */ private static boolean getSignVeryfy(Map<String, String> Params, String sign) throws AlipayApiException { // 過濾空值、sign與sign_type參數 Map<String, String> sParaNew = paraFilter(Params); // 獲取待簽名字符串 String preSignStr = createLinkString(sParaNew); // 得到簽名驗證結果 boolean isSign = false; isSign = AlipaySignature.rsaCheck(preSignStr, sign, AlipayConfig.PUBLIC_KEY, DEFAULT_CHARSET, AlipayConfig.APP_SIGN_TYPE); return isSign; } /** * 除去數組中的空值和簽名參數 * * @param sArray * 簽名參數組 * @return 去掉空值與簽名參數後的新簽名參數組 */ public static Map<String, String> paraFilter(Map<String, String> sArray) { Map<String, String> result = new HashMap<String, String>(); if (sArray == null || sArray.size() <= 0) { return result; } for (String key : sArray.keySet()) { String value = sArray.get(key); if (value == null || value.equals("") || key.equalsIgnoreCase("sign") || key.equalsIgnoreCase("sign_type")) { continue; } result.put(key, value); } return result; } /** * 把數組全部元素排序,並按照「參數=參數值」的模式用「&」字符拼接成字符串 * * @param params * 須要排序並參與字符拼接的參數組 * @return 拼接後字符串 */ public static String createLinkString(Map<String, String> params) { List<String> keys = new ArrayList<String>(params.keySet()); Collections.sort(keys); String prestr = ""; for (int i = 0; i < keys.size(); i++) { String key = keys.get(i); String value = params.get(key); if (i == keys.size() - 1) {// 拼接時,不包括最後一個&字符 prestr = prestr + key + "=" + value; } else { prestr = prestr + key + "=" + value + "&"; } } return prestr; } }
/*** * 對支付寶參數封裝 * @throws UnsupportedEncodingException * */ public String encapData(AlipayDto alipayDto) throws UnsupportedEncodingException { Map<String,Object> map = new HashMap<String, Object>(); /** * biz_content數據 */ Map<String,Object> biz_map = new HashMap<String, Object>(); /** * 業務參數 */ biz_map.put("body", alipayDto.getBody()); biz_map.put("subject", alipayDto.getSubject()); biz_map.put("out_trade_no", alipayDto.getOut_trade_no()); biz_map.put("timeout_express", alipayDto.getTimeout_express()); biz_map.put("total_amount", alipayDto.getTotal_amount()); biz_map.put("product_code", AlipayConfig.APP_PRODUCT_CODE); biz_map.put("passback_params", alipayDto.getPassback_params()); JSONObject bizcontentJson= JSONObject.fromObject(AlipayUtil.isVlaueNull(biz_map)); /** * 公共參數 */ map.put("app_id", alipayDto.getApp_id()); map.put("method", AlipayConfig.APP_METHOD); map.put("format", AlipayConfig.APP_FORMAT); map.put("charset", AlipayConfig.APP_CHARSET); map.put("sign_type", AlipayConfig.APP_SIGN_TYPE); map.put("timestamp", AlipayUtil.getDateTime(new Date())); map.put("version", AlipayConfig.APP_VERSION); map.put("notify_url", AlipayConfig.APP_NOTIFY_URL); map.put("biz_content", bizcontentJson.toString()); //簽名 String sign = AlipayUtil.getSign(AlipayUtil.isVlaueNull(map),AlipayConfig.PRIVATE_KEY); //編碼 String result = AlipayUtil.getReqestMap(AlipayUtil.isVlaueNull(map)); return result + sign; }
AlipayDto 是我封裝的支付寶參數類
package com.yuanxinxinxi.yuanxinbuluo.alipay.dto; /** * * 支付寶下單參數 */ public class AlipayDto { private String app_id; //支付寶分配給開發者的應用ID private String method;// 接口名稱 private String format; // 僅支持JSON private String charset;//請求使用的編碼格式,如utf-8,gbk,gb2312等 private String sign_type;//商戶生成簽名字符串所使用的簽名算法類型 private String timestamp;//發送請求的時間 private String version;//調用的接口版本,固定爲:1.0 private String notify_url;//支付寶服務器主動通知商戶服務器url private String biz_content;//業務請求參數的集合,最大長度不限 private String body;//描述信息。 private String subject;//商品的標題 private String out_trade_no;//商戶網站惟一訂單號 private String timeout_express;//交易的超時時間 private String total_amount;//訂單總金額 private String product_code;//銷售產品碼 QUICK_MSECURITY_PAY private String passback_params;//公用回傳參數 public String getApp_id() { return app_id; } public void setApp_id(String app_id) { this.app_id = app_id; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public String getFormat() { return format; } public void setFormat(String format) { this.format = format; } public String getCharset() { return charset; } public void setCharset(String charset) { this.charset = charset; } public String getSign_type() { return sign_type; } public void setSign_type(String sign_type) { this.sign_type = sign_type; } public String getTimestamp() { return timestamp; } public void setTimestamp(String timestamp) { this.timestamp = timestamp; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getNotify_url() { return notify_url; } public void setNotify_url(String notify_url) { this.notify_url = notify_url; } public String getBiz_content() { return biz_content; } public void setBiz_content(String biz_content) { this.biz_content = biz_content; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public String getOut_trade_no() { return out_trade_no; } public void setOut_trade_no(String out_trade_no) { this.out_trade_no = out_trade_no; } public String getTimeout_express() { return timeout_express; } public void setTimeout_express(String timeout_express) { this.timeout_express = timeout_express; } public String getTotal_amount() { return total_amount; } public void setTotal_amount(String total_amount) { this.total_amount = total_amount; } public String getProduct_code() { return product_code; } public void setProduct_code(String product_code) { this.product_code = product_code; } public String getPassback_params() { return passback_params; } public void setPassback_params(String passback_params) { this.passback_params = passback_params; } }
encapData方法返回結果給app起調就好了
支付完成後異步通知處理 將支付請求中拿到參數
/** * 支付寶獲取請求參數 * @param request * @return Map<String,String> */ public static Map<String,String> alipay_notify_para(HttpServletRequest request){ Map<String,String> map = new HashMap<String, String>(); Map<String,String[]> resultMap = request.getParameterMap(); for (Iterator<String> iter = resultMap.keySet().iterator(); iter.hasNext();) { String name = (String) iter.next(); String[] values = (String[]) resultMap.get(name); String valueStr = ""; for (int i = 0; i < values.length; i++) { valueStr = (i == values.length - 1) ? valueStr + values[i]:valueStr + values[i] + ","; } log.debug("支付寶回調參數:key="+name+"value:"+valueStr); map.put(name,valueStr); } return map; }
public Map<String, Object> notifyAlipayPayment(Map<String,String> map) { Map<String,Object> resultMap = new HashMap<String, Object>(); // 商戶訂單號 try { String notify_id = String.valueOf(map.get("notify_id")); String trade_status = String.valueOf(map.get("trade_status")); String sign_type = String.valueOf(map.get("sign_type")); /** * 是否有返回ID */ if(notify_id != null && !notify_id.equals("")){ /** * 驗證簽名 */ try { if(AlipaySignature.rsaCheckV1(map, AlipayConfig.PUBLIC_KEY ,AlipayConfig.APP_CHARSET,sign_type)){ /** * 支付是否成功 */ if(trade_status.equals("TRADE_SUCCESS") || trade_status.equals("TRADE_FINISHED")){ /** * 拿到參數數據 */ //金額 BigDecimal total_fee = new BigDecimal(String.valueOf(map.get("total_amount"))); //支付寶訂單號 String trade_no = String.valueOf(map.get("trade_no")); // 商戶訂單號 String out_trade_no = String.valueOf(map.get("out_trade_no")); //數據包 String passback_params = String.valueOf(map.get("passback_params")); //支付方Id String buyer_id = String.valueOf(map.get("buyer_id"));
---------------- 業務--------------------------
}else{ resultMap.put("msg","支付失敗"); resultMap.put("success", false); //總撤銷 } }else{ resultMap.put("msg","簽名錯誤"); resultMap.put("success", false); //總撤銷 } } catch (AssertionError e){ resultMap.put("msg",e.getMessage()); resultMap.put("success", false); } catch (AlipayApiException e) { resultMap.put("msg","簽名錯誤"); resultMap.put("success", false); log.error(e.getMessage()); //總撤銷 } }else{ resultMap.put("msg","返回url有誤"); resultMap.put("success", false); //總撤銷 } } catch (Exception e) { resultMap.put("msg","服務器未響應"); resultMap.put("success", false); //總撤銷 }
支付包退款很簡單 官網有代碼我這裏就不提供了,支付寶撤銷接口好像會報錯 ,一直報isv權限不足,後來撤銷我改爲退款後端
後臺回調在這裏說下如何通知客戶端api
1.回調後參數拿到,返回過來商戶訂單號查詢,看看錶中是否有這個支付訂單,若是有不作插入,若是沒有插入表數據 最後返回通知支付寶數組
2.通知支付寶後,客戶端能夠請求後臺,看看數據庫是否有該訂單號存在,若是存在說明支付完成,若是沒有主動查詢支付寶支付訂單,查詢返回結果插入數據,安全
3.插入數據失敗,請求支付寶退款服務器