公司首個(gè)外包項(xiàng)目的退換貨流程的退款操作是走原路返回的,而我有幸負(fù)責(zé)這個(gè)功能模塊開(kāi)發(fā)束凑,在實(shí)踐中總結(jié)了一些經(jīng)驗(yàn)挣菲,特此分享出來(lái)富稻。
一、支付寶退款白胀。
支付寶提供了有密以及普通退款接口椭赋,兩者的區(qū)別就是前者不需要申請(qǐng)即時(shí)到賬支付而后者需要,本文使用了有密退款接口或杠;支付寶四要素:PID(合作者id)哪怔,private_key(私鑰,我使用的是RSA加密方式生成的)向抢、alipay_public_key(公鑰认境,支付寶提供的)以及支付賬號(hào)。
1.1先看配置文件:
// 合作身份者ID挟鸠,簽約賬號(hào)叉信,以2088開(kāi)頭由16位純數(shù)字組成的字符串
public static String partner = "";
// 收款支付寶賬號(hào),以2088開(kāi)頭由16位純數(shù)字組成的字符串艘希,一般情況下收款賬號(hào)就是簽約賬號(hào)public static String seller_user_id = partner;
// 商戶(hù)的私鑰,需要PKCS8格式硼身,RSA公私鑰生成
public static String private_key = "";
// 支付寶公鑰
public static String alipay_public_key ?= "";
// 服務(wù)器異步通知頁(yè)面路徑 ?需http://格式的完整路徑,不能加?id=123這類(lèi)自定義參數(shù)覆享,必須外網(wǎng)可以正常訪問(wèn)
public static String notify_url = "http://xxx.xxxx.com/api/refund/refundBack_001";
1.2關(guān)鍵代碼:
/** * 退款操作(請(qǐng)求) * @param response * @return * @throws Exception ?* trade_no 支付寶返回的交易號(hào) * money ?退款金額 */
public String aliRefund(HttpServletRequest request, HttpServletResponse response, String trade_no, String money,String refundId,String batch_no) throws Exception {PrintWriter out = ControllerUtils.getPrintWriter(response, logger, "------------AlipayRefundActionMultiController.aliRefund start------------");
//服務(wù)器異步通知頁(yè)面路徑
?String notify_url;
?String sHtmlText = "";
?try {MapsParaTemp = new HashMap();
?//退款詳細(xì)數(shù)據(jù)鸠姨,必填,格式(支付寶交易號(hào)^退款金額^備注)淹真,多筆請(qǐng)用#隔開(kāi)
?String detail_data = trade_no + "^" + money + "^退款";
?sParaTemp.put("detail_data", detail_data);//退款詳情
?sParaTemp.put("service", AlipayConfig.service);//
?sParaTemp.put("partner", AlipayConfig.partner);//合作者id
?sParaTemp.put("_input_charset", AlipayConfig.input_charset);//字符集格式
sParaTemp.put("notify_url", AlipayConfig.notify_url);//回調(diào)地址
sParaTemp.put("seller_email", "商家賬號(hào)");//賬號(hào)
sParaTemp.put("refund_date", UtilDate.getDateFormatter());//退款時(shí)間
sParaTemp.put("batch_no", batch_no);//批次號(hào),必填连茧,格式:當(dāng)天日期[8位]+序列號(hào)[3至24位]核蘸,如:201603081000001
sParaTemp.put("batch_num", "1"); //退款筆數(shù)巍糯,必填,參數(shù)detail_data的值中客扎,“#”字符出現(xiàn)的數(shù)量加1祟峦,最大支持1000筆(即“#”字符出現(xiàn)的數(shù)量999個(gè))
sHtmlText = AlipaySubmit.buildRequest(sParaTemp, "get", "確認(rèn)");
} catch (Exception e) {
e.printStackTrace();
}
return sHtmlText;
}
注意:此處的交易號(hào)必須是你支付時(shí)保存的交易號(hào),以及退款金額不能大于實(shí)付金額且不能<=0徙鱼,否則無(wú)法調(diào)起支付宅楞。我是將退款信息用一個(gè)form表單封裝起來(lái)然后以流的方式輸出,前端頁(yè)面只需要拿到該信息然后填充到一個(gè)div中袱吆。
1.3表單封裝:
/** * 建立請(qǐng)求厌衙,以表單HTML形式構(gòu)造(默認(rèn)) * @param sParaTemp 請(qǐng)求參數(shù)數(shù)組 * @param strMethod 提交方式。兩個(gè)值可選:post绞绒、get * @param strButtonName 確認(rèn)按鈕顯示文字 * @return 提交表單HTML文本 */
public static String buildRequest(Map < String, String > sParaTemp, String strMethod, String strButtonName){ ? ?//待請(qǐng)求參數(shù)數(shù)組 ? ?
Map < String, ? ?String > sPara = buildRequestPara(sParaTemp); ? ?
List < String > keys = new ArrayList < String > (sPara.keySet()); ? ? ? ?
StringBuffer sbHtml = new StringBuffer(); ? ? ?
sbHtml.append(""); ? ? ? ?
for (int i = 0; i < keys.size(); i++) ? ?{ ? ? ?
String name = (String)keys.get(i); ? ? ? ?
String value = (String)sPara.get(name); ? ? ? ? ? ? ?
sbHtml.append(""); ? ?
} ? ?
//submit按鈕控件請(qǐng)不要含有name屬性 ? ?
sbHtml.append(""); ? ?
sbHtml.append("document.forms['alipaysubmit'].submit();"); ? ? ? ?
return sbHtml.toString();}
1.4前端頁(yè)面拿到信息后進(jìn)行填充:
1.5效果展示:
二婶希、微信退款。
2.1配置文件
2.2退款前提條件:
商戶(hù)必須開(kāi)通微信支付功能蓬衡,開(kāi)發(fā)人員要拿到賬號(hào)以及證書(shū)(重點(diǎn))喻杈,退款金額不能大于實(shí)付價(jià)、小于等于0狰晚,并且傳入的實(shí)際支付價(jià)必須跟微信那邊保持一致筒饰,否則無(wú)法完成退款操作。
2.2關(guān)鍵代碼:
private String doRefundByWX(String money, String bankSerialNumber, long numberCount, String no,String sumAmout) throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, IOException{
String result="";
String appid=WeiXinUtil.appid;//應(yīng)用ID
String mch_id=WeiXinUtil.mch_id;//商戶(hù)號(hào)
String nonce_str=WeiXinUtil.CreateNoncestr();//隨機(jī)字符串
String transaction_id=bankSerialNumber;//微信訂單號(hào)
String out_refund_no=no;//商戶(hù)退款單號(hào)
Double total_fee=0d;
try {
total_fee = StringUtil.getDouble(sumAmout);
} catch (NumberFormatException e)
{e.printStackTrace();}
catch (Exception e) {
e.printStackTrace();
}
long totalAmount = new BigDecimal(total_fee * 100d).longValue();//總金額以分為單位Double refund_fee=Double.parseDouble(money);
long Amount = new BigDecimal(refund_fee * 100d).longValue();//退款金額以分為單位
String op_user_id=WeiXinUtil.mch_id;//操作員帳號(hào), 默認(rèn)為商戶(hù)號(hào) /簽名算法SortedMapSortedMap = new TreeMap();
SortedMap.put("appid", appid);
SortedMap.put("mch_id",mch_id);
SortedMap.put("nonce_str",nonce_str);
SortedMap.put("transaction_id", transaction_id);
SortedMap.put("out_refund_no", out_refund_no);
SortedMap.put("total_fee",String.valueOf(totalAmount));
SortedMap.put("refund_fee", String.valueOf(Amount));
SortedMap.put("op_user_id", op_user_id);
String sign=WeiXinUtil.createSign("UTF-8",SortedMap);
//獲取最終待發(fā)送的數(shù)據(jù)
String requestXml=WeiXinUtil.getRequestXml(SortedMap);
//(2)建立連接并發(fā)送數(shù)據(jù)
result=WeixinSendPost(requestXml);
return result;
}
2.3退款操作:
先申明一點(diǎn):微信退款沒(méi)有提供跳轉(zhuǎn)頁(yè)面壁晒,也就是一點(diǎn)擊退款瓷们,只要信息正確就會(huì)自動(dòng)打錢(qián)到退款賬戶(hù)。
三讨衣、關(guān)于保存微信支付换棚、支付寶交易號(hào)作為退款字段的代碼圖(訂單支付的回調(diào)方法):
備注:由于時(shí)間問(wèn)題就不上全部代碼,如若要具體代碼請(qǐng)關(guān)注我反镇,如果響應(yīng)激烈我將會(huì)將代碼放到GitHub固蚤。