支付宝支付-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
支付流程
支付文档参考:支付文档,支付文档2APP支付:服务器端按照文档【统一收单交易支付接口】创建支付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支付
相关文章推荐
- 支付宝APP支付服务端详解(JAVA)
- 支付宝支付-APP支付服务端详解
- 支付宝支付——app支付服务端详解
- PHP服务端支付宝app支付遇到的坑
- 支付宝APP支付集成,服务端(JAVA)
- php 服务端集成支付宝APP支付
- 支付宝官网下载App支付服务端DEMO&S(Java、.net、php 4000 )
- 服务端支付宝App支付对接笔记
- java服务端–支付宝APP支付接口
- Java服务端接入支付宝APP支付及微信APP支付
- 支付宝app支付java服务端
- PHP服务端集成支付宝APP支付以及回调
- app支付宝支付java服务端代码
- APP 支付宝支付,服务端处理
- java服务端–支付宝APP支付接口
- 支付宝APP支付集成,服务端(JAVA)
- 支付宝app支付服务端流程
- 支付宝APP支付Android与IONIC与服务端,服务端类库从android端考取
- 支付宝app支付(php服务端)
- java--springboot支付宝新版app支付服务端代码