前段时间开发了一个商城,集成了支付宝,微信,网银支付(接入的是连连支付第三方支付接口)。现在总结一下。
首先到https://www.alipay.com/ 注册一个支付宝商户号,进入开放平台,创建一个应用,签约电脑网站支付
接着配置应用信息:
这里的授权回调地址填的是支付宝付款成功后异步回调的地址,应用网关我填的是同步回调地址。下面的应用公钥需要自己设置
代码里面的配置:
package com.rongding.config;
import java.io.FileWriter;
import java.io.IOException;
/* *
*类名:AlipayConfig
*功能:基础配置类
*详细:设置帐户有关信息及返回路径
*修改日期:2017-04-05
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
public class AlipayConfig {
//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
public static String app_id = "";
// 商户私钥,您的PKCS8格式RSA2私钥
public static String merchant_private_key = "";
// 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
public static String alipay_public_key = "";
// 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String notify_url = "";
// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String return_url = "";
// 签名方式
public static String sign_type = "RSA2";
// 字符编码格式
public static String charset = "utf-8";
// 支付宝网关 ,沙箱测试时使用 alipaydev 生产环境使用alipay
public static String gatewayUrl = "https://openapi.alipay.com/gateway.do";
// 支付宝网关
public static String log_path = "C:\\";//可以不填
// 商户uid
public static String seller_id = "";
//↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
/**
* 写日志,方便测试(看网站需求,也可以改成把记录存入数据库)
* @param sWord 要写入日志里的文本内容
*/
public static void logResult(String sWord) {
FileWriter writer = null;
try {
writer = new FileWriter(log_path + "alipay_log_" + System.currentTimeMillis()+".txt");
writer.write(sWord);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
@Controller
public class OrderController {
@Autowired
private HttpServletRequest request;
@Autowired
private ReqProductService reqproductService;
@Autowired
private TransreqService transreqService;
@Autowired
private AddressService addressService;
@Autowired
private OrderService orderService;
private Logger log = Logger.getLogger(OrderController.class);
private String sendToAlipay(HttpServletRequest request,Map map) throws AlipayApiException{
Transreq transreq = (Transreq) map.get("transreq");
ReqProduct reqproduct = (ReqProduct) map.get("reqproduct");
//获得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
//设置请求参数
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
alipayRequest.setReturnUrl(AlipayConfig.return_url);
alipayRequest.setNotifyUrl(AlipayConfig.notify_url);
//商户订单号,商户网站订单系统中唯一订单号,必填
String out_trade_no = new String(transreq.getSerialNo());
//付款金额,必填
String total_amount = new String(DataUtil.formatDouble(transreq.getAmt().doubleValue(), 2));
//订单名称,必填
String subject = new String(reqproduct.getPrdName());
//商品描述,可空
String body = new String(reqproduct.getPrdName());
alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
+ "\"total_amount\":\""+ total_amount +"\","
+ "\"subject\":\""+ subject +"\","
+ "\"body\":\""+ body +"\","
+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//若想给BizContent增加其他可选请求参数,以增加自定义超时时间参数timeout_express来举例说明
//alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
// + "\"total_amount\":\""+ total_amount +"\","
// + "\"subject\":\""+ subject +"\","
// + "\"body\":\""+ body +"\","
// + "\"timeout_express\":\"10m\","
// + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//请求参数可查阅【电脑网站支付的API文档-alipay.trade.page.pay-请求参数】章节
//请求
String result = alipayClient.pageExecute(alipayRequest).getBody();
return result;
}
@RequestMapping("aliPayNotify_url")
@ResponseBody
public String alipaynotify(Model model,HttpServletRequest request,HttpServletResponse response){
log.info("支付宝异步回调 ------------beg-----------");
/* *
* 功能:支付宝服务器异步通知页面
* 日期:2017-03-30
* 说明:
* 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
* 该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*************************页面功能说明*************************
* 创建该页面文件时,请留心该页面文件中无任何HTML代码及空格。
* 该页面不能在本机电脑测试,请到服务器上做测试。请确保外部可以访问该页面。
* 如果没有收到该页面返回的 success
* 建议该页面只做支付成功的业务逻辑处理,退款的处理请以调用退款查询接口的结果为准。
*/
String result = "fail";
//获取支付宝POST过来反馈信息
Map params = new HashMap();
Map requestParams = request.getParameterMap();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
//乱码解决,这段代码在出现乱码时使用
// try {
// valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
// } catch (UnsupportedEncodingException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
params.put(name, valueStr);
}
String contentS = AlipayFunction.CreateLinkString(params);
log.debug("支付宝异步回调请求参数:"+contentS);
boolean signVerified =false;
try {
signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);
} catch (AlipayApiException e1) {
// TODO Auto-generated catch block
log.error("由于"+e1.getErrMsg()+"返回给支付宝系统的结果result:fail");
model.addAttribute("result", "fail");
return result;
} //调用SDK验证签名
//——请在这里编写您的程序(以下代码仅作参考)——
/* 实际验证过程建议商户务必添加以下校验:
1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
4、验证app_id是否为该商户本身。
*/
if(signVerified) {//验证成功
//商户订单号
//交易状态
log.info("支付宝异步回调验签成功!");
String trade_status = request.getParameter("trade_status");
if("TRADE_FINISHED".equals(trade_status)){
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//如果有做过处理,不执行商户的业务程序
//注意:
//退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
try {
dologic(request);
result = "success";
} catch (Exception e) {
log.error(e.getMessage());
result = "fail";
}
}else if ("TRADE_SUCCESS".equals(trade_status)){
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//如果有做过处理,不执行商户的业务程序
//注意:
//付款完成后,支付宝系统发送该交易状态通知
try {
dologic(request);
result = "success";
} catch (Exception e) {
// TODO Auto-generated catch block
log.error(e.getMessage());
result = "fail";
}
}else{
result = "fail";
}
// out.println("success");
}else {//验证失败
// out.println("fail");
result = "fail";
//调试用,写文本函数记录程序运行情况是否正常
//String sWord = AlipaySignature.getSignCheckContentV1(params);
//AlipayConfig.logResult(sWord);
log.debug("支付宝异步回调验签失败");
}
log.debug("异步回调返回给支付宝系统的结果result:"+result);
model.addAttribute("result", result);
log.info("支付宝异步回调 -------------end ------------");
return result;
}
private void dologic(HttpServletRequest request) throws Exception{
log.info("付款成功后处理商户业务逻辑 --------------beg--------------");
//商户系统订单号
String out_trade_no = request.getParameter("out_trade_no");
//支付宝交易号
String trade_no = request.getParameter("trade_no");
//金额
String total_amount = request.getParameter("total_amount");
//app_id
String app_id = request.getParameter("app_id");
Transreq transreq = transreqService.selectByPrimaryKey(out_trade_no);//购买流水
if(transreq == null){
throw new Exception("未查询到购买流水信息,商户逻辑处理失败,订单号为:"+out_trade_no);
}
if(!IDict.REQ_STATUS.REQ_STATUS_DFK.equals(transreq.getStatus())){//判断状态
throw new Exception("该购买流水信息状态不为待付款,商户逻辑处理失败,订单号为:"+out_trade_no);
}
if(transreq.getAmt().subtract(new BigDecimal(total_amount)).compareTo(new BigDecimal("0.1"))>0){//判断金额
throw new Exception("支付宝支付支付的金额与订单中的金额不一致,商户逻辑处理失败,订单号为:"+out_trade_no);
};
if(!AlipayConfig.app_id.equals(app_id)){//判断appid是否相同
throw new Exception("支付宝支付异步反回的appid与商户appid不一致,商户逻辑处理失败,相应的订单号为:"+out_trade_no);
}
Transreq uploadtransreq = transreqService.selectByPrimaryKey(transreq.getAssoSerial());
if(uploadtransreq == null){
throw new Exception("支付宝支付异步回调时未找到与订单信息相关的上传商品的信息,商户逻辑处理失败,订单号为:"+out_trade_no);
}
if(!IDict.REQ_STATUS.REQ_STATUS_SUCCESS.equals(uploadtransreq.getStatus())){
throw new Exception("支付宝支付异步回调时找到的与订单信息相关的上传商品的信息状态不正确,商户逻辑处理失败,订单号为:"+out_trade_no);
}
try {
orderService.pay(transreq, uploadtransreq,trade_no);
} catch (Exception e) {
throw new RuntimeException("数据更新失败,订单号为:"+out_trade_no);
}
log.info("付款成功后处理商户业务逻辑 --------------end--------------");
return ;
}
@RequestMapping("aliPayReturn_url")
public String returnUrl(Model model,HttpServletRequest request,HttpServletResponse response){
log.info("支付宝同步回调 ------------------beg--------------------");
/* *
* 功能:支付宝服务器同步通知页面
* 日期:2017-03-30
* 说明:
* 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
* 该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*************************页面功能说明*************************
* 该页面仅做页面展示,业务逻辑处理请勿在该页面执行
*/
String result = null;
//获取支付宝GET过来反馈信息
Map params = new HashMap();
Map requestParams = request.getParameterMap();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
// //乱码解决,这段代码在出现乱码时使用
// try {
// valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
// } catch (UnsupportedEncodingException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
params.put(name, valueStr);
// System.out.println(name+":"+valueStr);
}
String contentS = AlipayFunction.CreateLinkString(params);
log.debug("支付宝同步回调请求参数:"+contentS);
boolean signVerified = false;
try {
signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);
} catch (AlipayApiException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} //调用SDK验证签名
//——请在这里编写您的程序(以下代码仅作参考)——
if(signVerified) {
//商户订单号
String out_trade_no = request.getParameter("out_trade_no");
//支付宝交易号
String trade_no = request.getParameter("trade_no");
//付款金额
String total_amount = request.getParameter("total_amount");
result = "trade_no:"+trade_no+"
out_trade_no:"+out_trade_no+"
total_amount:"+total_amount;
// out.println("trade_no:"+trade_no+"
out_trade_no:"+out_trade_no+"
total_amount:"+total_amount);
log.debug("支付宝同步回调验签成功");
}else {
// out.println("验签失败");
result = "验签失败";
log.debug("支付宝同步回调验签失败");
}
model.addAttribute("result", result);
log.info("支付宝同步回调 ------------------end--------------------");
return "return_url";
}
}
大致就是这样,支付宝的api给的还是比较好理解的,但是支付宝支付需要审核,审核的时候需要网站在外网上能够访问到。