您的位置:首页 > 移动开发

支付宝支付-APP支付服务端详解

2016-11-18 13:58 639 查看

支付宝APP支付服务端详解

前面接了微信支付,相比微信支付,支付宝APP支付提供了支付分装类,下面将实现支付宝APP支付、订单查询、支付结果异步通知、APP支付申请参数说明,以及服务端返回APP端发起支付的签名、商户私钥、支付宝公钥的配置使用等。

支付注意事项

1、APP支付不能在沙箱测试、只能申请上线测试

2、需要创建RSA密钥设置文档,设置后上传rsa_public_key.pem【开发者公钥,上传时需要去掉公钥的头和尾】上传成功后换取支付宝公钥,为项目的alipay_public_key.pem

3、rsa_private_key_pkcs8.pem【开发者私钥】,去掉头和尾为项目的alipay_private_key_pkcs8.pem

4、需要导入所需支付包:alipay-sdk-java.jar 和 commons-logging.jar,具体参考:服务端SDK

支付流程

支付文档参考:支付文档支付文档2

APP支付:服务器端按照文档【统一收单交易支付接口】创建支付OrderStr返回APP端——-APP端拿到OrderStr发起支付—–支付宝服务器端回调服务端异步通知接口——-服务器端按照【App支付结果异步通知】校验签名等做业务逻辑处理

APP支付订单查询:服务器端调用【统一收单线下交易查询】查询支付订单

APP支付申请退款:每笔支付可以申请多次退款,但退款总金额不能超过支付金额,调用【统一收单交易退款接口】发起退款申请

APP支付退款查询:服务端调用【 统一收单交易退款查询】查询退款订单信息

支付项目结构

支付项目demo结构如下



支付代码实现

支付代码

PayController.java :包含支付、支付查询、异步通知、退款申请、退款查询

package org.andy.alipay.controller;

import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.andy.alipay.model.JsonResult;
import org.andy.alipay.model.ResponseData;
import org.andy.alipay.util.AlipayUtil;
import org.andy.alipay.util.DatetimeUtil;
import org.andy.alipay.util.PayUtil;
import org.andy.alipay.util.SerializerFeatureUtil;
import org.andy.alipay.util.StringUtil;
import org.andy.alipay.util.WebUtil;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayConstants;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;

/**
* 创建时间:2016年11月2日 下午4:16:32
*
* @author andy
* @version 2.2
*/
@Controller
@RequestMapping("/order")
public class PayController {

private static final Logger LOG = Logger.getLogger(PayController.class);

/**
* 支付下订单
*
* @param request
* @param response
* @param cashnum
*            支付金额
* @param mercid
*            商品id
* @param callback
*/
@RequestMapping(value = "/pay", method = RequestMethod.POST)
public void orderPay(HttpServletRequest request, HttpServletResponse response,
@RequestParam(required = false, defaultValue = "0") Double cashnum, String mercid, String callback) {
LOG.info("[/order/pay]");
if (!"001".equals(mercid)) {
WebUtil.response(response, WebUtil.packJsonp(callback, JSON
.toJSONString(new JsonResult(-1, "商品不存在", new ResponseData()), SerializerFeatureUtil.FEATURES)));
}

Map<String, String> param = new HashMap<>();
// 公共请求参数
param.put("app_id", AlipayUtil.ALIPAY_APPID);// 商户订单号
param.put("method", "alipay.trade.app.pay");// 交易金额
param.put("format", AlipayConstants.FORMAT_JSON);
param.put("charset", AlipayConstants.CHARSET_UTF8);
param.put("timestamp", DatetimeUtil.formatDateTime(new Date()));
param.put("version", "1.0");
param.put("notify_url", "https://www.andy.org/alipay/order/pay/notify.shtml"); // 支付宝服务器主动通知商户服务
param.put("sign_type", AlipayConstants.SIGN_TYPE_RSA);

Map<String, Object> pcont = new HashMap<>();
// 支付业务请求参数
pcont.put("out_trade_no", PayUtil.getTradeNo()); // 商户订单号
pcont.put("total_amount", String.valueOf(cashnum));// 交易金额
pcont.put("subject", "测试支付"); // 订单标题
pcont.put("body", "Andy");// 对交易或商品的描述
pcont.put("product_code", "QUICK_MSECURITY_PAY");// 销售产品码

param.put("biz_content", JSON.toJSONString(pcont)); // 业务请求参数  不需要对json字符串转义
Map<String, String> payMap = new HashMap<>();
try {
param.put("sign", PayUtil.getSign(param, AlipayUtil.APP_PRIVATE_KEY)); // 业务请求参数
payMap.put("orderStr", PayUtil.getSignEncodeUrl(param, true));
} catch (Exception e) {
e.printStackTrace();
}

WebUtil.response(response, WebUtil.packJsonp(callback, JSON.toJSONString(
new JsonResult(1, "订单获取成功", new ResponseData(null, payMap)), SerializerFeatureUtil.FEATURES)));
}

/**
*
* @param request
* @param response
* @param tradeno
*            支付宝订单交易编号
* @param orderno
*            商家交易编号
* @param callback
*/
@RequestMapping(value = "/pay/query", method = RequestMethod.POST)
public void orderPayQuery(HttpServletRequest request, HttpServletResponse response, String tradeno, String orderno,
String callback) {
LOG.info("[/order/pay/query]");
if (StringUtil.isEmpty(tradeno) && StringUtil.isEmpty(orderno)) {
WebUtil.response(response, WebUtil.packJsonp(callback, JSON
.toJSONString(new JsonResult(-1, "订单号不能为空", new ResponseData()), SerializerFeatureUtil.FEATURES)));
}

AlipayTradeQueryRequest alipayRequest = new AlipayTradeQueryRequest(); // 统一收单线下交易查询
// 只需要传入业务参数
Map<String, Object> param = new HashMap<>();
param.put("out_trade_no", orderno); // 商户订单号
param.put("trade_no", tradeno);// 交易金额
alipayRequest.setBizContent(JSON.toJSONString(param)); // 不需要对json字符串转义

Map<String, String> restmap = new HashMap<String, String>();// 返回提交支付宝订单交易查询信息
boolean flag = false; // 查询状态
try {
AlipayTradeQueryResponse alipayResponse = AlipayUtil.getAlipayClient().execute(alipayRequest);
if (alipayResponse.isSuccess()) {
// 调用成功,则处理业务逻辑
if ("10000".equals(alipayResponse.getCode())) {
// 订单创建成功
flag = true;
restmap.put("order_no", alipayResponse.getOutTradeNo());
restmap.put("trade_no", alipayResponse.getTradeNo());
restmap.put("buyer_logon_id", alipayResponse.getBuyerLogonId());
restmap.put("trade_status", alipayResponse.getTradeStatus());
LOG.info("订单查询结果:" + alipayResponse.getTradeStatus());
} else {
LOG.info("订单查询失败:" + alipayResponse.getMsg() + ":" + alipayResponse.getSubMsg());
}
}
} catch (AlipayApiException e) {
e.printStackTrace();
}

if (flag) {
// 订单查询成功
WebUtil.response(response,
WebUtil.packJsonp(callback,
JSON.toJSONString(new JsonResult(1, "订单查询成功", new ResponseData(null, restmap)),
SerializerFeatureUtil.FEATURES)));
} else { // 订单查询失败
WebUtil.response(response, WebUtil.packJsonp(callback, JSON
.toJSONString(new JsonResult(-1, "订单查询失败", new ResponseData()), SerializerFeatureUtil.FEATURES)));
}
}

/**
* 订单支付微信服务器异步通知
*
* @param request
* @param response
*/
@RequestMapping(value = "/pay/notify", method = RequestMethod.POST)
public void orderPayNotify(HttpServletRequest request, HttpServletResponse response) {
LOG.info("[/order/pay/notify]");
// 获取到返回的所有参数 先判断是否交易成功trade_status 再做签名校验
// 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
// 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
// 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email),
// 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。
if ("TRADE_SUCCESS".equals(request.getParameter("trade_status"))) {
Enumeration<?> pNames = request.getParameterNames();
Map<String, String> param = new HashMap<String, String>();
try {
while (pNames.hasMoreElements()) {
String pName = (String) pNames.nextElement();
param.put(pName, request.getParameter(pName));
}

boolean signVerified = AlipaySignature.rsaCheckV1(param, AlipayUtil.ALIPAY_PUBLIC_KEY,
AlipayConstants.CHARSET_UTF8); // 校验签名是否正确
if (signVerified) {
// TODO 验签成功后
// 按照支付结果异步通知中的描述,对支付结果中的业务内容进行1\2\3\4二次校验,校验成功后在response中返回success,校验失败返回failure
LOG.info("订单支付成功:" + JSON.toJSONString(param));
} else {
// TODO 验签失败则记录异常日志,并在response中返回failure.
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

/**
* 订单退款
*
* @param request
* @param response
* @param tradeno
*            支付宝交易订单号
* @param orderno
*            商家交易订单号
* @param callback
*/
@RequestMapping(value = "/pay/refund", method = RequestMethod.POST)
public void orderPayRefund(HttpServletRequest request, HttpServletResponse response, String tradeno, String orderno,
String callback) {
LOG.info("[/pay/refund]");
if (StringUtil.isEmpty(tradeno) && StringUtil.isEmpty(orderno)) {
WebUtil.response(response, WebUtil.packJsonp(callback, JSON
.toJSONString(new JsonResult(-1, "订单号不能为空", new ResponseData()), SerializerFeatureUtil.FEATURES)));
}

AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest(); // 统一收单交易退款接口
// 只需要传入业务参数
Map<String, Object> param = new HashMap<>();
param.put("out_trade_no", orderno); // 商户订单号
param.put("trade_no", tradeno);// 交易金额
param.put("refund_amount", 0.01);// 退款金额
param.put("refund_reason", "测试支付退款");// 退款金额
param.put("out_request_no", PayUtil.getRefundNo()); //退款单号
alipayRequest.setBizContent(JSON.toJSONString(param)); // 不需要对json字符串转义

Map<String, Object> restmap = new HashMap<>();// 返回支付宝退款信息
boolean flag = false; // 查询状态
try {
AlipayTradeRefundResponse alipayResponse = AlipayUtil.getAlipayClient().execute(alipayRequest);
if (alipayResponse.isSuccess()) {
// 调用成功,则处理业务逻辑
if ("10000".equals(alipayResponse.getCode())) {
// 订单创建成功
flag = true;
restmap.put("out_trade_no", alipayResponse.getOutTradeNo());
restmap.put("trade_no", alipayResponse.getTradeNo());
restmap.put("buyer_logon_id", alipayResponse.getBuyerLogonId());// 用户的登录id
restmap.put("gmt_refund_pay", alipayResponse.getGmtRefundPay()); // 退看支付时间
restmap.put("buyer_user_id", alipayResponse.getBuyerUserId());// 买家在支付宝的用户id
LOG.info("订单退款结果:退款成功");
} else {
LOG.info("订单查询失败:" + alipayResponse.getMsg() + ":" + alipayResponse.getSubMsg());
}
}
} catch (AlipayApiException e) {
e.printStackTrace();
}

if (flag) {
// 订单查询成功
WebUtil.response(response,
WebUtil.packJsonp(callback,
JSON.toJSONString(new JsonResult(1, "订单退款成功", new ResponseData(null, restmap)),
SerializerFeatureUtil.FEATURES)));
} else { // 订单查询失败
WebUtil.response(response, WebUtil.packJsonp(callback, JSON
.toJSONString(new JsonResult(-1, "订单退款失败", new ResponseData()), SerializerFeatureUtil.FEATURES)));
}
}

/**
*
* @param request
* @param response
* @param orderno
*            商家订单号
* @param tradeno
*            支付宝订单号
* @param callback
*/
@RequestMapping(value = "/pay/refund/query", method = RequestMethod.POST)
public void orderPayRefundQuery(HttpServletRequest request, HttpServletResponse response, String orderno,
String tradeno, String callback) {
LOG.info("[/pay/refund/query]");
if (StringUtil.isEmpty(orderno) && StringUtil.isEmpty(tradeno)) {
WebUtil.response(response,
WebUtil.packJsonp(callback,
JSON.toJSONString(new JsonResult(-1, "商家订单号或支付宝订单号不能为空", new ResponseData()),
SerializerFeatureUtil.FEATURES)));
}

AlipayTradeFastpayRefundQueryRequest alipayRequest = new AlipayTradeFastpayRefundQueryRequest(); // 统一收单交易退款查询
// 只需要传入业务参数
Map<String, Object> param = new HashMap<>();
param.put("out_trade_no", orderno); // 商户订单号
param.put("trade_no", tradeno);// 交易金额
param.put("out_request_no", orderno);// 请求退款接口时,传入的退款请求号,如果在退款请求时未传入,则该值为创建交易时的外部交易号
alipayRequest.setBizContent(JSON.toJSONString(param)); // 不需要对json字符串转义

Map<String, Object> restmap = new HashMap<>();// 返回支付宝退款信息
boolean flag = false; // 查询状态
try {
AlipayTradeFastpayRefundQueryResponse alipayResponse = AlipayUtil.getAlipayClient().execute(alipayRequest);
if (alipayResponse.isSuccess()) {
// 调用成功,则处理业务逻辑
if ("10000".equals(alipayResponse.getCode())) {
// 订单创建成功
flag = true;
restmap.put("out_trade_no", alipayResponse.getOutTradeNo());
restmap.put("trade_no", alipayResponse.getTradeNo());
restmap.put("out_request_no", alipayResponse.getOutRequestNo());// 退款订单号
restmap.put("refund_reason", alipayResponse.getRefundReason()); // 退款原因
restmap.put("total_amount", alipayResponse.getTotalAmount());// 订单交易金额
restmap.put("refund_amount", alipayResponse.getTotalAmount());// 订单退款金额
LOG.info("订单退款结果:退款成功");
} else {
LOG.info("订单失败:" + alipayResponse.getMsg() + ":" + alipayResponse.getSubMsg());
}
}
} catch (AlipayApiException e) {
e.printStackTrace();
}

if (flag) {
// 订单查询成功
WebUtil.response(response,
WebUtil.packJsonp(callback,
JSON.toJSONString(new JsonResult(1, "订单退款成功", new ResponseData(null, restmap)),
SerializerFeatureUtil.FEATURES)));
} else { // 订单查询失败
WebUtil.response(response, WebUtil.packJsonp(callback, JSON
.toJSONString(new JsonResult(-1, "订单退款失败", new ResponseData()), SerializerFeatureUtil.FEATURES)));
}
}

}


签名代码

SignUtils.java:支付宝支付签名实现

package org.andy.alipay.util;

import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;

public class SignUtils {

private static final String ALGORITHM = "RSA";

private static final String SIGN_ALGORITHMS = "SHA1WithRSA";

private static final String DEFAULT_CHARSET = "UTF-8";

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_ALGORITHMS);

signature.initSign(priKey);
signature.update(content.getBytes(DEFAULT_CHARSET));

byte[] signed = signature.sign();

return new String(Base64.encode(signed));
} catch (Exception e) {
e.printStackTrace();
}

return null;
}

}


Ali支付工具类

AlipayUtil.java:初始化AlipayClient、开发者私有、支付宝公钥等

package org.andy.alipay.util;

import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConstants;
import com.alipay.api.DefaultAlipayClient;

/**
* 创建时间:2016年11月10日 下午7:09:08
*
* alipay支付
*
* @author andy
* @version 2.2
*/

public class AlipayUtil {

public static final String ALIPAY_APPID = ConfigUtil.getProperty("alipay.appid"); // appid

public static String APP_PRIVATE_KEY = null; // app支付私钥

public static String ALIPAY_PUBLIC_KEY = null; // 支付宝公钥

static {
try {
Resource resource = new ClassPathResource("alipay_private_key_pkcs8.pem");
APP_PRIVATE_KEY = FileUtil.readInputStream2String(resource.getInputStream());
resource = new ClassPathResource("alipay_public_key.pem");
ALIPAY_PUBLIC_KEY = FileUtil.readInputStream2String(resource.getInputStream());
} catch (Exception e) {
e.printStackTrace();
}
}

// 统一收单交易创建接口
private static AlipayClient alipayClient = null;

public static AlipayClient getAlipayClient() {
if (alipayClient == null) {
synchronized (AlipayUtil.class) {
if (null == alipayClient) {
alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", ALIPAY_APPID,
APP_PRIVATE_KEY, AlipayConstants.FORMAT_JSON, AlipayConstants.CHARSET_UTF8,
ALIPAY_PUBLIC_KEY);
}
}
}
return alipayClient;
}
}


支付工具类

PayUtil.java:生成签名、订单生成等

package org.andy.alipay.util;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import com.alipay.api.AlipayConstants;

/**
* 创建时间:2016年11月2日 下午7:12:44
*
* @author andy
* @version 2.2
*/

public class PayUtil {

/**
* 生成订单号
*
* @return
*/
public static String getTradeNo() {
// 自增8位数 00000001
return "TNO" + DatetimeUtil.formatDate(new Date(), DatetimeUtil.TIME_STAMP_PATTERN) + "00000001";
}

/**
* 退款单号
*
* @return
*/
public static String getRefundNo() {
// 自增8位数 00000001
return "RNO" + DatetimeUtil.formatDate(new Date(), DatetimeUtil.TIME_STAMP_PATTERN) + "00000001";
}

/**
* 退款单号
*
* @return
*/
public static String getTransferNo() {
// 自增8位数 00000001
return "TNO" + DatetimeUtil.formatDate(new Date(), DatetimeUtil.TIME_STAMP_PATTERN) + "00000001";
}

/**
* 返回客户端ip
*
* @param request
* @return
*/
public static String getRemoteAddrIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (StringUtil.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
// 多次反向代理后会有多个ip值,第一个ip才是真实ip
int index = ip.indexOf(",");
if (index != -1) {
return ip.substring(0, index);
} else {
return ip;
}
}
ip = request.getHeader("X-Real-IP");
if (StringUtil.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
return ip;
}
return request.getRemoteAddr();
}

/**
* 获取服务器的ip地址
*
* @param request
* @return
*/
public static String getLocalIp(HttpServletRequest request) {
return request.getLocalAddr();
}

/**
* 创建支付随机字符串
*
* @return
*/
public static String getNonceStr() {
return RandomUtil.randomString(RandomUtil.LETTER_NUMBER_CHAR, 32);
}

/**
* 支付时间戳
*
* @return
*/
public static String payTimestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}

/**
* 返回签名编码拼接url
*
* @param params
* @param isEncode
* @return
*/
public static String getSignEncodeUrl(Map<String, String> map, boolean isEncode) {
String sign = map.get("sign");
String encodedSign = "";
if (CollectionUtil.isNotEmpty(map)) {
map.remove("sign");
List<String> keys = new ArrayList<String>(map.keySet());
// key排序
Collections.sort(keys);

StringBuilder authInfo = new StringBuilder();

boolean first = true;// 是否第一个
for (String key: keys) {
if (first) {
first = false;
} else {
authInfo.append("&");
}
authInfo.append(key).append("=");
if (isEncode) {
try {
authInfo.append(URLEncoder.encode(map.get(key), AlipayConstants.CHARSET_UTF8));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else {
authInfo.append(map.get(key));
}
}

try {
encodedSign = authInfo.toString() + "&sign=" + URLEncoder.encode(sign, AlipayConstants.CHARSET_UTF8);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}

return encodedSign.replaceAll("\\+", "%20");
}

/**
* 对支付参数信息进行签名
*
* @param map
*            待签名授权信息
*
* @return
*/
public static String getSign(Map<String, String> map, String rsaKey) {
List<String> keys = new ArrayList<String>(map.keySet());
// key排序
Collections.sort(keys);

StringBuilder authInfo = new StringBuilder();
boolean first = true;
for (String key : keys) {
if (first) {
first = false;
} else {
authInfo.append("&");
}
authInfo.append(key).append("=").append(map.get(key));
}

return SignUtils.sign(authInfo.toString(), rsaKey);
}

}


支付结果

以下为支付宝支付和支付宝退款





支付宝App支付测试完成

项目源码地址:支付宝APP支付
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息