支付宝有密退款MD5和RSA两种方式
前面我已经做了手机端和电脑端的支付,这两天整了一下退款,万万没想到啊,一样的道理我却走了半天弯路麻蛋啊,MD5和RSA两种方式我全试了,单笔数据集detail_data写对了就好使了。。。,多谢支付宝接入人员的提醒
先看一下 官方文档,技术人员给我的,很有用的:https://docs.open.alipay.com/62/104744
说的已经很明白了,不可为空的你就传参就是了,关于代码方面有不懂的看我 支付宝手机网页唤醒app支付 逻辑代码以及log走的流程和参数都在这,
我只说需要改动的地方,后续会提供demo,还有记得先支付,无支付,不退款
MD5方式:
AlipayConfig中调用的接口名:public static String service = "refund_fastpay_by_platform_pwd";
并且加一个 //退款日期 时间格式 yyyy-MM-dd HH:mm:ss——public static String refund_date = UtilDate.getDateFormatter(); 后续会用到
index 退款界面和支付界面当然不一样
<% /* * *功能:支付宝即时到账交易接口调试入口页面 *版本:3.4 *日期:2016-03-08 *说明: *以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。 ********************************************** */ %> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>支付宝即时到账批量退款有密接口</title> </head> <style> html,body { width:100%; min-width:1200px; height:auto; padding:0; margin:0; font-family:"微软雅黑"; background-color:#242736 } .header { width:100%; margin:0 auto; height:230px; background-color:#fff } .container { width:100%; min-width:100px; height:auto } .black { background-color:#242736 } .blue { background-color:#0ae } .qrcode { width:1200px; margin:0 auto; height:30px; background-color:#242736 } .littlecode { width:16px; height:16px; margin-top:6px; cursor:pointer; float:right } .showqrs { top:30px; position:absolute; width:100px; margin-left:-65px; height:160px; display:none } .shtoparrow { width:0; height:0; margin-left:65px; border-left:8px solid transparent; border-right:8px solid transparent; border-bottom:8px solid #e7e8eb; margin-bottom:0; font-size:0; line-height:0 } .guanzhuqr { text-align:center; background-color:#e7e8eb; border:1px solid #e7e8eb } .guanzhuqr img { margin-top:10px; width:80px } .shmsg { margin-left:10px; width:80px; height:16px; line-height:16px; font-size:12px; color:#242323; text-align:center } .nav { width:1200px; margin:0 auto; height:70px; } .open,.logo { display:block; float:left; height:40px; width:85px; margin-top:20px } .divier { display:block; float:left; margin-left:20px; margin-right:20px; margin-top:23px; width:1px; height:24px; background-color:#d3d3d3 } .open { line-height:30px; font-size:20px; text-decoration:none; color:#1a1a1a } .navbar { float:right; width:200px; height:40px; margin-top:15px; list-style:none } .navbar li { float:left; width:100px; height:40px } .navbar li a { display:inline-block; width:100px; height:40px; line-height:40px; font-size:16px; color:#1a1a1a; text-decoration:none; text-align:center } .navbar li a:hover { color:#00AAEE } .title { width:1200px; margin:0 auto; height:80px; line-height:80px; font-size:20px; color:#FFF } .content { width:100%; min-width:1200px; height:660px; background-color:#fff; } .alipayform { width:800px; margin:0 auto; height:500px; border:1px solid #0ae } .element { width:600px; height:80px; margin-left:100px; font-size:20px } .etitle,.einput { float:left; height:26px } .etitle { width:150px; line-height:26px; text-align:right } .einput { width:200px; margin-left:20px } .einput input { width:398px; height:24px; border:1px solid #0ae; font-size:16px } .mark { margin-top: 10px; width:500px; height:30px; margin-left:80px; line-height:30px; font-size:12px; color:#999 } .legend { margin-left:100px; font-size:24px } .alisubmit { width:400px; height:40px; border:0; background-color:#0ae; font-size:16px; color:#FFF; cursor:pointer; margin-left:170px } .footer { width:100%; height:120px; background-color:#242735 } .footer-sub a,span { color:#808080; font-size:12px; text-decoration:none } .footer-sub a:hover { color:#00aeee } .footer-sub span { margin:0 3px } .footer-sub { padding-top:40px; height:20px; width:600px; margin:0 auto; text-align:center } </style> <body> <div class="header"> <div class="container black"> <div class="qrcode"> <div class="littlecode"> <img width="16px" src="../img/little_qrcode.jpg" id="licode"> <div class="showqrs" id="showqrs"> <div class="shtoparrow"></div> <div class="guanzhuqr"> <img src="../img/guanzhu_qrcode.png" width="80"> <div class="shmsg" style="margin-top:5px;"> 请扫码关注 </div> <div class="shmsg" style="margin-bottom:5px;"> 接收重要信息 </div> </div> </div> </div> </div> </div> <div class="container"> <div class="nav"> <a href="https://www.alipay.com/" class="logo"><img src="../img/alipay_logo.png" height="30px"></a> <span class="divier"></span> <a href="http://open.alipay.com/platform/home.htm" class="open" target="_blank">开放平台</a> <ul class="navbar"> <li><a href="https://doc.open.alipay.com/doc2/detail?treeId=62&articleId=103566&docType=1" target="_blank">在线文档</a></li> <li><a href="https://cschannel.alipay.com/portal.htm?sourceId=213" target="_blank">技术支持</a></li> </ul> </div> </div> <div class="container blue"> <div class="title">即时到账批量退款有密接口(refund_fastpay_by_platform_pwd)</div> </div> </div> <div class="content"> <form action="alipayapi.jsp" class="alipayform" method="POST" target="_blank"> <div class="element" style="margin-top:60px;"> <div class="legend">支付宝即时到账批量退款有密接口快速通道 </div> </div> <div class="element"> <div class="etitle">退款批次号:</div> <div class="einput"><input type="text" name="WIDbatch_no" id="batch_no"></div> <br> <div class="mark">注意:退款批次号(batch_no),必填(时间格式是yyyyMMddHHmmss+数字或者字母)</div> </div> <div class="element"> <div class="etitle">退款笔数:</div> <div class="einput"><input type="text" name="WIDbatch_num" value="1"></div> <br> <div class="mark">注意:退款笔数(batch_num),必填(值为您退款的笔数,取值1~1000间的整数)</div> </div> <div class="element"> <div class="etitle">退款详细数据:</div> <div class="einput"><input type="text" name="WIDdetail_data" value="2017082921001004740215337654^1^home"></div> <br> <div class="mark">注意:退款详细数据(WIDdetail_data),必填(支付宝交易号^退款金额^备注)多笔请用#隔开</div> </div> <div class="element"> <input type="submit" class="alisubmit" value ="确认"> </div> </form> </div> <div class="footer"> <p class="footer-sub"> <a href="http://ab.alipay.com/i/index.htm" target="_blank">关于支付宝</a><span>|</span> <a href="https://e.alipay.com/index.htm" target="_blank">商家中心</a><span>|</span> <a href="https://job.alibaba.com/zhaopin/index.htm" target="_blank">诚征英才</a><span>|</span> <a href="http://ab.alipay.com/i/lianxi.htm" target="_blank">联系我们</a><span>|</span> <a href="#" id="international" target="_blank">International Business</a><span>|</span> <a href="http://ab.alipay.com/i/jieshao.htm#en" target="_blank">About Alipay</a> <br> <span>支付宝版权所有</span> <span class="footer-date">2004-2016</span> <span><a href="http://fun.alipay.com/certificate/jyxkz.htm" target="_blank">ICP证:沪B2-20150087</a></span> </p> </div> </body> <script> var even = document.getElementById("licode"); var showqrs = document.getElementById("showqrs"); even.onmouseover = function(){ showqrs.style.display = "block"; } even.onmouseleave = function(){ showqrs.style.display = "none"; } </script> </html>
话说我就不知道那个batch_no退款批次号是什么,其实是自己定义的,还以为是订单号。。。按他说的格式定义就行,还需要退款笔数和单笔数据集detail_data,这里我已经给出了示范
然后也是请求到alipayapi
<% ////////////////////////////////////请求参数////////////////////////////////////// //批次号,必填,格式:当天日期[8位]+序列号[3至24位],如:201603081000001 String batch_no = new String(request.getParameter("WIDbatch_no").getBytes("ISO-8859-1"),"UTF-8"); //退款笔数,必填,参数detail_data的值中,“#”字符出现的数量加1,最大支持1000笔(即“#”字符出现的数量999个) String batch_num = new String(request.getParameter("WIDbatch_num").getBytes("ISO-8859-1"),"UTF-8"); //退款详细数据,必填,格式(支付宝交易号^退款金额^备注),多笔请用#隔开 String detail_data = new String(request.getParameter("WIDdetail_data").getBytes("ISO-8859-1"),"UTF-8"); ////////////////////////////////////////////////////////////////////////////////// //把请求参数打包成数组 Map<String, String> sParaTemp = new HashMap<String, String>(); sParaTemp.put("service", AlipayConfig.service); sParaTemp.put("partner", AlipayConfig.partner); sParaTemp.put("_input_charset", AlipayConfig.input_charset); sParaTemp.put("MD5", AlipayConfig.sign_type); sParaTemp.put("notify_url", AlipayConfig.notify_url); sParaTemp.put("seller_user_id", AlipayConfig.seller_user_id); sParaTemp.put("refund_date", AlipayConfig.refund_date); sParaTemp.put("batch_no", batch_no); sParaTemp.put("batch_num", batch_num); sParaTemp.put("detail_data", detail_data); //建立请求 String sHtmlText = AlipaySubmit.buildRequest(sParaTemp,"get","确认"); out.println(sHtmlText); %>
该打包的打包,就按文档需求的那个整,然后请求buildRequest去签名并且发送链接就行了,其他都一样
话说我走的弯路在哪呢,没找到一个好的文档所以就没有规范,然后我开始用的RSA方式,并且那个detail_data格式不对,大家不要和我犯一样的错误哦,正确的单笔退款方式是:订单号也就是流水号也就是交易号^钱数^退款说明,这个确实挺重要,卡个跟头
RSA方式
这个方式麻烦的地方在于先生成公匙私匙,好吧,说说怎么生成的吧
生成工具,很好使的生成工具,关于这方面的说明也不少了,直接去这看看吧http://blog.csdn.net/zhouhui520w/article/details/49885135,说的挺详细的,注意pkcs8格式的生成并且无空格无回车,将公匙上传到支付宝中,我相信你能上传上去,有一步是私匙验证公匙是否匹配,不用管它,前面做对了就一定是匹配的
AlipayConfig中我贴一下代码:
public class AlipayConfig { //↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // 合作身份者ID,签约账号,以2088开头由16位纯数字组成的字符串,查看地址:https://b.alipay.com/order/pidAndKey.htm public static String partner = ""; // 收款支付宝账号,以2088开头由16位纯数字组成的字符串,一般情况下收款账号就是签约账号 public static String seller_user_id = partner; // 商户的私钥,需要PKCS8格式,RSA公私钥生成:https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.nBDxfy&treeId=58&articleId=103242&docType=1 public static String private_key = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAt97DHzJ+6ETQ8AY/k31hrF9L1wZVr/kpiF2gnP/sOAIZqNioR6sZMh54kdBZB8+jWjOhpMHZfqJ3JI2A+5mCs0tUl4rBpWmww8OjEGRoixamCc6Hsr9/Gn4pMUMURtVMEXdHeLNQPr/xNd7dbYKxRYiGoCZAVAgMBAAECgYEArdX6OmhnpxLCBdrNZD0B/icLBU1WqtScpJAIMQZLrzcN+XEzKiN4JKxDyWLN7PIHBoIpIDX9a0OLO3uDfcVqJ1eL1hMPDsvlxV72FWzUlkFZcBdVgm800CjB3F2z414zPNGWbo2/RQOp9JkEnnF0ekdOqdMBufoQk3W9ofv/UMECQQD7KXatki/rB6HxDXY3tjg91zE69hTJKvYLN8Cq/rkrAoT5XU2fcwg2CyRXVGgc+DlS+zKf5Bchcp2i6yQK+A3xAkEA1VAwWCCv6WE+Rrbe4aLkWc6p+57E90LPo3a6u96aCIBeZT0oTp2lXlQ4gmWCH6jFeeQHxOUJ8FvIWL+hLx0QZQI/LSFGMeoSsiis8KN6mogjkvkX34BYO818aIecFx2uKAxL2ILrb7s7vnpCDW8dRhdUNZTtg814Woo9yQh3cYEhAkEAukZyUSMKOtTuWzYqYAOtQTeVfONiwUGYrPhrjM+Q10eyJtpHLo+/7yXAyy31ecwZ1p1oo2oXScv8onOFP+0zeQJBAMGyDyZqBcbzrUVFRbSAMvno8GqEflNuPJv4/uiy5pI0qHn0GA/dmab/lUX7BA4dsXyoBoRHyheeYgo9J0lwUtQ="; // 支付宝公钥,查看地址:https://b.alipay.com/order/pidAndKey.htm public static String alipay_public_key = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRSCwhfuhE0PAGP5N9YaxfS9cGVa/5KYhdoJz/7DgCGajYqEerGTIeeJHQWQfPo1ozoaTB2X6idySNgPuZgrNLVJeKwaVpsMPDoxBkaIsWpgnOh7K/fxp+KTFDFEbVTBF3R3izUD6/8TXe3W2CsUWIhqAmQFQIDAQAB"; // 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 public static String notify_url = "http://域名/Refund/jsp/notify_url.jsp"; // 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 public static String return_url = "http://域名/Refund/jsp/return_url.jsp"; // 签名方式 public static String sign_type = "RSA"; // 调试用,创建TXT日志文件夹路径,见AlipayCore.java类中的logResult(String sWord)打印方法。 public static String log_path = "C:\"; // 字符编码格式 目前支持 gbk 或 utf-8 public static String input_charset = "utf-8"; //退款日期 时间格式 yyyy-MM-dd HH:mm:ss public static String refund_date = UtilDate.getDateFormatter(); // 调用的接口名,无需修改 public static String service = "refund_fastpay_by_platform_pwd"; //↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ }
上面的信息形式是正确的,签名方式是RSA,将公匙和pkcs8私匙上传,要无空格无回车,再贴一下RSA.java
package com.alipay.sign; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import javax.crypto.Cipher; public class RSA{ public static final String SIGN_ALGORITHMS = "SHA1WithRSA"; /** * RSA签名 * @param content 待签名数据 * @param privateKey 商户私钥 * @param input_charset 编码格式 * @return 签名值 */ public static String sign(String content, String privateKey, String input_charset) { try { PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec( Base64.decode(privateKey) ); KeyFactory keyf = KeyFactory.getInstance("RSA"); PrivateKey priKey = keyf.generatePrivate(priPKCS8); java.security.Signature signature = java.security.Signature .getInstance(SIGN_ALGORITHMS); signature.initSign(priKey); signature.update( content.getBytes(input_charset) ); byte[] signed = signature.sign(); return Base64.encode(signed); } catch (Exception e) { e.printStackTrace(); } return null; } /** * RSA验签名检查 * @param content 待签名数据 * @param sign 签名值 * @param ali_public_key 支付宝公钥 * @param input_charset 编码格式 * @return 布尔值 */ public static boolean verify(String content, String sign, String ali_public_key, String input_charset) { try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); byte[] encodedKey = Base64.decode(ali_public_key); PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey)); java.security.Signature signature = java.security.Signature .getInstance(SIGN_ALGORITHMS); signature.initVerify(pubKey); signature.update( content.getBytes(input_charset) ); boolean bverify = signature.verify( Base64.decode(sign) ); return bverify; } catch (Exception e) { e.printStackTrace(); } return false; } /** * 解密 * @param content 密文 * @param private_key 商户私钥 * @param input_charset 编码格式 * @return 解密后的字符串 */ public static String decrypt(String content, String private_key, String input_charset) throws Exception { PrivateKey prikey = getPrivateKey(private_key); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, prikey); InputStream ins = new ByteArrayInputStream(Base64.decode(content)); ByteArrayOutputStream writer = new ByteArrayOutputStream(); //rsa解密的字节大小最多是128,将需要解密的内容,按128位拆开解密 byte[] buf = new byte[128]; int bufl; while ((bufl = ins.read(buf)) != -1) { byte[] block = null; if (buf.length == bufl) { block = buf; } else { block = new byte[bufl]; for (int i = 0; i < bufl; i++) { block[i] = buf[i]; } } writer.write(cipher.doFinal(block)); } return new String(writer.toByteArray(), input_charset); } /** * 得到私钥 * @param key 密钥字符串(经过base64编码) * @throws Exception */ public static PrivateKey getPrivateKey(String key) throws Exception { byte[] keyBytes; keyBytes = Base64.decode(key); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(keySpec); return privateKey; } }
这就是RSA的加密方式了,需要Base64.java工具类有点长就不贴了,在demo中有,其他的都一样了
退款两种加密方式的demo 对照着看一下就懂了,我相信你可以的
这个demo忘了在哪找的了,就按这个做就行,真是没做过就不会,做过就很简单
- 上一篇: 支付宝签名验证机制
- 下一篇: 商城支付~支付宝~加密