前面我已经做了手机端和电脑端的支付,这两天整了一下退款,万万没想到啊,一样的道理我却走了半天弯路麻蛋啊,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忘了在哪找的了,就按这个做就行,真是没做过就不会,做过就很简单