支付宝支付之“单笔转账到支付宝账户接口”的调用(生成签名、上传应用公钥、下载SDK、接口调用、报错自动排查、查看错误码)
支付宝接口调用
“单笔转账到支付宝账户”的接口调用,一般涉及到下面几个知识点
1、生成签名
在使用支付宝接口的时候,需要使用支付宝的签名,这里需要使用支付宝的RSA生成工具。
关于生成签名的网址:https://docs.open.alipay.com/291/105971/
从网址上可以下载Windows和MAC版本的RSA签名生成工具
下载该工具后,解压打开文件夹,运行“RSA签名验签工具.bat”(WINDOWS)或“RSA签名验签工具.command”(MAC_OSX),目录结构如下:
其中界面类似如下:
RSA签名算法生成的格式
RSA2签名算法生成的格式:
概念介绍:
应用私钥:由商户自己生成的RSA私钥,商户开发者使用应用私钥对字符串进行加签。
应用公钥:是由商户自己生成的RSA公钥,商户需要上传到应用公钥到支付宝开放平台以便支付宝验证该公钥是否是商户自己发起的。
支付宝公钥:支付宝的RSA公钥,商户使用支付宝公钥验证结果是否是支付宝返回的。
详细步骤:
1、根据开发语言选择**格式。
2、选择**长度,建议使用2048位。
3、点击”生成**”,会自动生成商户应用公钥和应用私钥。
4、点击”打开**文件路径”,即可找到生成的公私钥。找到上图最下面提示的路径,可以找到:”应用公钥XXX.txt”和”应用私钥XXX.txt”,截图如下:
生成的私钥需要妥善保管,避免遗失,不要泄露。应用私钥需要填写到代码*签名时使用。应用公钥需要提供给支付宝账号管理者上传到支付宝开放平台。
TIPS:除了使用支付宝提供的一键生成**工具外,也可以使用OpenSSL工具命令生成**。网址:https://docs.open.alipay.com/291/106130
2、上传应用公钥并获取支付宝公钥
官方文档:https://docs.open.alipay.com/291/105972
以下将支付宝的这块儿的内容罗列到本文中:
注意:如果想上传应用公钥然后去获取支付宝公钥,这里用户需要是支付宝账号管理者
1. 点击签名验签工具右下角的“上传公钥”会打开支付宝开放平台网页,输入账号登录。(建议使用IE或Chrome浏览器。)
2. 在“我的应用”中,选择要配置**的应用,点击“查看”。记录对应的APPID(下图红框处),在代码中使用。
关于笔者个人的这个页面显示的效果如下:
若首次登录管理中心,请根据引导填写所需信息完成开发者入驻:
第一步:点击认证,完成实名认证。
第二步:点击编辑,完善信息。
根据引导流程,完成手机绑定。
第三步:签署协议,完成入驻。
- 在“应用环境”-“接口加签方式”下方点击“设置应用公钥”。
注:接口中的sign_type参数应与上传**的加签方式一致。例如接口参数中sign_type=RSA2,请求时就会使用此处设置的RSA2(SHA256)公钥验签。
建议使用RSA2加签方式。
根据开发者的条件设置应用公钥或上传公钥证书,常规请点击设置应用公钥。
若未绑定手机,请根据引导,完成手机绑定。
完成手机短信验证。
把签名验签工具里“公钥”的内容复制到此处,点击“保存”完成**设置。
保存成功后,在同一页面查看或修改应用公钥或上传应用公钥证书。
保存支付宝公钥内容,在代码中验签使用。
3、下载SDK
关于支付宝支付,有多个界面可供下载,下载地址:
https://docs.open.alipay.com/54/103419/
下载后的SDK的文件结构:
4、单笔转账到支付宝账户接口调用
关于支付宝单笔转账到支付宝接口的调用,接口文档地址:https://docs.open.alipay.com/api_28/alipay.fund.trans.toaccount.transfer
通过接口文档,可以了解到如下内容:
公共参数(请求地址、公共请求参数)
请求参数
公共响应参数
响应参数
请求示例
响应示例
异常示例
业务错误码
其中查看业务错误码是比较重要的一个排查问题的手段。
实际项目中的调用案例:
package com.xxxx.pay.service;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayFundTransToaccountTransferRequest;
import com.alipay.api.response.AlipayFundTransToaccountTransferResponse;
import com.xxxx.comm.cache.impl.SysConfig;
import com.xxxx.comm.enumset.BusinessCodeEnum;
import com.xxxx.comm.util.PayOrderNumUtils;
import com.xxxx.dubbo.facade.soaIntf.pay.IAlipayService;
import com.xxxx.pay.data.beans.TbPayExtractCallbackRecord;
import com.xxxx.pay.data.mapper.TbPayExtractCallbackRecordMapper;
import com.xxxx.redis.core.RedisUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p class="detail">
* 功能:提现审核中支付宝自动打款
* </p>
* @ClassName: AlipayService
* @version V1.0
* @date 2017-12-17
* @author tuzq
* Copyright 2017 xxxx.com, Inc. All rights reserved
*/
@Service(value = "alipayService")
public class AlipayService implements IAlipayService {
private static Logger logger = LoggerFactory.getLogger(AlipayService.class);
@Autowired
RedisTemplate<Serializable, Object> baseRedisTemplate;
@Resource(name = "payExtractCallbackRecordMapper")
TbPayExtractCallbackRecordMapper payExtractCallbackRecordMapper;
/**
* <p class="detail">
* 功能:转账到支付宝账户
* </p>
* @author tuzq
* @date 2017-12-17
* @param receiptAccountName :收款账号名
* @param receiptAccountNumber:收款账号,支付宝账号
* @param remittanceMoney :收款金额
* @param ip :操作提现审核的机器的ip
* @param extractId :提现记录表中的id
* @param accountId :收款钱包账户表中的id
* @param userId :提现操作用户ID
* @param name :审核用户的用户名
* @param isDeleted :0:正常,1:删除
* @return
*/
public String pay2Alipay(
String receiptAccountName,
String receiptAccountNumber,
Double remittanceMoney,
String ip,
Long extractId,
Long accountId,
Long userId,
String name,
Integer isDeleted) {
//其中包含:code,msg,data三类信息,其中code = 0表示转账失败,1:转账成功,data中包含转账记录中的值
Map<String,Object> map = new HashMap<String,Object>();
//判断指定分钟内是否是同一个支付宝提现,如果是十分钟之内是同一个支付宝提现,则直接提示提现失败
//0:没有转账过,或没有转账成功过 1:10分钟内不能连续提现转账 2:已经转账过,不能重复提现转账
int isAccountIsWithdrawDeposited = judgeAccountIsWithdrawDeposited(1,receiptAccountNumber,extractId);
if (1 == isAccountIsWithdrawDeposited) {
map.put("code", 0);
map.put("msg","同一账户不能在10分钟内连续提现!");
map.put("data","");
return JSONObject.toJSONString(map);
}
if (2 == isAccountIsWithdrawDeposited) {
map.put("code",0);
map.put("msg","提现申请已受理并成功转账,不能重复提现转账!");
map.put("data","");
return JSONObject.toJSONString(map);
}
TbPayExtractCallbackRecord payExtractCallbackRecord = new TbPayExtractCallbackRecord();
try {
AlipayClient alipayClient = new DefaultAlipayClient(
//即:https://openapi.alipay.com/gateway.do
SysConfig.getValue("alipay_serverUrl"),
//即:用用的appid
SysConfig.getValue("alipay_appId"),
//即:通过验签工具生成的"商户 应用私钥" ,注意:
//1、这里若选用的是1024的,下面的alipay_alipayPulicKey"支付宝应用公钥"要使用 "支付宝账号管理者"上传应用公钥 后 的RSA后面对应的"支付宝公钥",alipay_signType 这个值配置成RSA
//2、这里若选用的是2048的,下面的alipay_alipayPulicKey"支付宝应用公钥"要使用 "支付宝账号管理者"上传应用公钥 后 的RSA2(SHA256)后面的"支付宝公钥",alipay_signType 这个值配置成RSA2
//上面推荐使用RSA2的签名方式
SysConfig.getValue("alipay_privateKey"),
"json",
"utf-8",
SysConfig.getValue("alipay_alipayPulicKey"),
SysConfig.getValue("alipay_signType"));
//注意:如果这里找不到这个API,需要将上文中下载的Alipay对应的SDK打到maven私服中去。具体打的方式:百度
AlipayFundTransToaccountTransferRequest request = new AlipayFundTransToaccountTransferRequest();
//创建一个唯一的businessCodeNum
String businessCodeNum = this.createUniqueBusinessCodeNum();
//businessCodeNum :相当于购买商品时的订单号,这个需要平台生成一个唯一的编号传递给Alipay接口
//ALIPAY_LOGONID :固定值
//receiptAccountNumber:即收款者支付宝账号
//receiptAccountName :支付宝账号中留的收款者真实名称
request.setBizContent("{" +
"'out_biz_no':'"+ businessCodeNum +"'," +
"'payee_type':'ALIPAY_LOGONID'," +
"'payee_account':'" + receiptAccountNumber + "'," +
"'amount':'" + remittanceMoney + "'," +
"'payer_show_name':'支付宝'," +
"'payee_real_name':'"+ receiptAccountName +"'," +
"'remark':'提现审核通过,平台转账到支付宝账户'}");
AlipayFundTransToaccountTransferResponse response = null;
response = alipayClient.execute(request);
String data = response.getBody();
JSONObject jsonObject = JSONObject.parseObject(data).getJSONObject("alipay_fund_trans_toaccount_transfer_response");
logger.info("--------------------支付宝返回参数 start---------------------");
logger.info(jsonObject.toJSONString());
logger.info("--------------------支付宝返回参数 start ---------------------");
if(response.isSuccess()){
//正确格式类似:{"alipay_fund_trans_toaccount_transfer_response":{"code":"10000","msg":"Success","order_id":"20171219110070001502670020659574","out_biz_no":"TX20171219112511712151872","pay_date":"2017-12-19 11:25:19"}
String code = jsonObject.getString("code");
String msg = jsonObject.getString("msg");
String orderId = jsonObject.getString("order_id");
String outBizNo = jsonObject.getString("out_biz_no");
String payDate = jsonObject.getString("pay_date");
//设置记录
payExtractCallbackRecord.setAliCode(code);
payExtractCallbackRecord.setAliMsg(msg);
payExtractCallbackRecord.setAliOrderId(orderId);
payExtractCallbackRecord.setAliOutBizNo(outBizNo);
payExtractCallbackRecord.setAliPayDate(payDate);
//设置返回的状态code
map.put("code",1);
map.put("msg","支付宝转账成功!");
} else {
//错误格式类似:{"alipay_fund_trans_toaccount_transfer_response":{"code":"40002","msg":"Invalid Arguments","sub_code":"isv.invalid-app-id","sub_msg":"无效的AppID参数"}}
String code = jsonObject.getString("code");
String msg = jsonObject.getString("msg");
String sub_code = jsonObject.getString("sub_code");
String sub_msg = jsonObject.getString("sub_msg");
//设置错误记录
payExtractCallbackRecord.setAliCode(code);
payExtractCallbackRecord.setAliMsg(msg);
payExtractCallbackRecord.setAliSubCode(sub_code);
payExtractCallbackRecord.setAliSubMsg(sub_msg);
payExtractCallbackRecord.setAliOutBizNo(businessCodeNum);
//设置返回的状态code
map.put("code",0);
map.put("msg",sub_msg);
RedisUtil redisUtil = new RedisUtil(baseRedisTemplate);
redisUtil.remove(receiptAccountNumber);
}
} catch (AlipayApiException e) {
logger.error("提现审核支付宝调用支付宝接口出错{}",e.getMessage());
} finally {
payExtractCallbackRecord.setReceiptAccountName(receiptAccountName);
payExtractCallbackRecord.setReceiptAccountNumber(receiptAccountNumber);
payExtractCallbackRecord.setIp(ip);
payExtractCallbackRecord.setAccountId(accountId);
payExtractCallbackRecord.setOptUserId(userId);
payExtractCallbackRecord.setOptUserName(name);
//获取时间的毫秒值
Long timeMillis = System.currentTimeMillis() * 1000;
payExtractCallbackRecord.setCreateTime(timeMillis);
payExtractCallbackRecord.setUpdateTime(timeMillis);
payExtractCallbackRecord.setIsDeleted(0);
payExtractCallbackRecord.setRemittanceMoney(new BigDecimal(remittanceMoney));
payExtractCallbackRecord.setExtractId(extractId);
map.put("data",payExtractCallbackRecord);
payExtractCallbackRecordMapper.insertTbPayExtractCallbackRecord(payExtractCallbackRecord);
}
//将对象转换成JSONObject对象,方便后续使用json.put(key,value)进行添加扩张
//JSONObject json = (JSONObject) JSONObject.toJSON(payExtractCallbackRecord);
return JSONObject.toJSONString(map);
}
/**
* 判断账号是否已经提现过了,场景:
* 1、如果在指定时间内,直接返回,提示指定时间内不可以重复提现
* 2、如果没有这个信息,从提现信息表中查询是否有同一个ExtractId且是成功的(code=10000,有支付宝返回的ali_order_id)提现记录,且未被删除
* @param specifiedTime :指定时间,单位是分钟
* @param receiptAccountNumber :账号名称
* @param extractId :订单编号
* @return 0:没有转账过,或没有转账成功过 1:10分钟内不能连续提现转账 2:已经转账过,不能重复提现转账
*/
int judgeAccountIsWithdrawDeposited(int specifiedTime, String receiptAccountNumber,Long extractId) {
RedisUtil redisUtil = new RedisUtil(baseRedisTemplate);
//如果已经存在,直接返回true
if (redisUtil.exists(receiptAccountNumber)) {
return 1;
}
//通过extract_id,ali_code,ali_order_id,is_deleted = 0
List<TbPayExtractCallbackRecord> payExtractCallbackRecordList = payExtractCallbackRecordMapper.selectPayExtractCallbackRecordList(extractId,0);
//如果也是空的,说明没有转账成功过,将account设置进去,并且过期时间使10分钟
if (payExtractCallbackRecordList.isEmpty()) {
redisUtil.set(receiptAccountNumber,receiptAccountNumber,specifiedTime);
//没有转账过,或没有成功过
return 0;
}
//已经转账过,不能重复提现转账
return 2;
}
/**
* 创建唯一的订单编号
* @return
*/
public String createUniqueBusinessCodeNum() {
String businessCodeNum = PayOrderNumUtils.createPayOrderNum(BusinessCodeEnum.ALIPAY_TX,100000);
//避免高并发场景下businessCodeNum相同的情况下
RedisUtil redisUtil = new RedisUtil(baseRedisTemplate);
if(redisUtil.exists(businessCodeNum)) {
return createUniqueBusinessCodeNum();
}
//设置10分钟内过期
redisUtil.set(businessCodeNum,businessCodeNum,10 * 60 * 1000);
return businessCodeNum;
}
}
5、调用报错时自动排查
在调用接口的时候,可能会报其它错误,可以从下面的网址中查找:
https://docs.open.alipay.com/291/106096/
解释的错误有:
6、RSA和RSA2签名算法区别
参考网址:https://docs.open.alipay.com/291/106115/
7、查看错误码
在调用接口的时候,会返回code,其中code的解释说明文档为:
https://docs.open.alipay.com/common/105806
类似:
8、将alipay的sdk打到本地maven仓库
参考网址:
http://blog.csdn.net/tototuzuoquan/article/details/78831835
下一篇: Maximum Profit