BlogJava 联系 聚合 管理  

Blog Stats

文章分类

文章档案


xwgong

向javaEE前进

 1 package com.alipay.client.base;
 2 
 3 public class PartnerConfig {
 4     
 5     //合作商户ID。用签约支付宝账号登录ms.alipay.com后,在账户信息页面获取。
 6     public static final String PARTNER = "2088011096718773";
 7     // 账户ID。用签约支付宝账号登录ms.alipay.com后,在账户信息页面获取。
 8     public static final String SELLER = "2088011096718773";
 9     // 商户(RSA)私钥
10     public static final String RSA_PRIVATE = "";
11                                                     
12     // 支付宝(RSA)公钥 用签约支付宝账号登录ms.alipay.com后,在密钥管理页面获取。
13     public static final String RSA_ALIPAY_PUBLIC = "";
14     
15 }
PartnerConfig.java为封装商家提供的属性类,参数全部由商户提供或者技术生成
PARTNER 与
SELLER 为商户提供的pid,可以再支付宝ms.alipay.com查看.
RSA_PRIVATE为支付宝提供的openssl-0.9.8k_WIN32(RSA密钥生成工具)生成商户私钥.根据商户私钥生成商户公钥(RSA密钥生成工具),把商户公钥在支付宝的私钥管理页面进行提交,支付宝会为商户提供一个支付宝公钥既RSA_ALIPAY_PUBLIC字段的值

package com.alipay.client.callback;

import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

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

import com.alipay.client.base.PartnerConfig;
import com.alipay.client.security.RSASignature;

/**
 * 安全支付支付完成商户同步处理程序
 * 对支付宝返回订单信息验签名
 
*/
public class RSACallBack extends HttpServlet {


    private static final long serialVersionUID = -2234271646410251381L;
    
    //签名成功
    public static final String RESULT_CHECK_SIGN_SUCCEED  = "T";
    //签名失败
    public static final String RESULT_CHECK_SIGN_FAILED = "F";
    
    @SuppressWarnings("unchecked")
    public void doPost(HttpServletRequest request, HttpServletResponse response)throws UnsupportedEncodingException {
        //获得待签名数据和签名值
        String sign = URLDecoder.decode(request.getParameter("sign"),"utf-8");
        String content = URLDecoder.decode(request.getParameter("content"),"utf-8");
        
        String retVal = RESULT_CHECK_SIGN_FAILED;
        //使用支付宝公钥验签名
        try {
            PrintWriter out = response.getWriter();
            if(RSASignature.doCheck(content, sign, PartnerConfig.RSA_ALIPAY_PUBLIC)){
                retVal=RESULT_CHECK_SIGN_SUCCEED;
            }
            
            response.setContentType("text/html");   
            System.out.println("retVal : " + retVal);
            out.print(retVal);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("验签名失败");
        }

    }
}
这个类基本不需要做修改,主要是RESULT_CHECK_SIGN_SUCCEED和RESULT_CHECK_SIGN_FAILED的值必须要与客户端相应代码的相应字段保持一致.这个类主要是支付完成之后客户端与服务器进行的一次验签名,可以用来更新数据给客户端.
/**
 * Alipay.com Inc.
 * Copyright (c) 2005-2008 All Rights Reserved.
 
*/
package com.alipay.client.notify;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Map;

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.airplane.base.AbstractExecution;
import com.airplane.dao.IntegralDao;
import com.airplane.dao.OrderDao;
import com.airplane.dao.UserDao;
import com.airplane.domain.Integral;
import com.airplane.domain.Order;
import com.airplane.domain.User;
import com.alipay.client.base.PartnerConfig;
import com.alipay.client.security.RSASignature;

/**
 * 接收通知并处理
 * 
 * 
@author 3y
 * 
@version $Id: NotifyReceiver.java, v 0.1 2011-8-15 下午03:11:58 3y Exp $
 
*/
public class RSANotifyReceiver extends HttpServlet {


    private static final long                          serialVersionUID = 7216412938937049671L;
    private static Log logger = LogFactory.getLog(RSANotifyReceiver.class);

    @SuppressWarnings("unchecked")
    public void doPost(HttpServletRequest request, HttpServletResponse response){
        System.out.println("接收到通知!");
        //获得通知参数
        try{
        Map map = request.getParameterMap();
        //获得通知签名
        String sign = (String) ((Object[]) map.get("sign"))[0];
        logger.info("sign="+sign);
        //获得待验签名的数据
        String verifyData = getVerifyData(map);
        logger.info("verifyData="+verifyData);
        boolean verified = false;
        //使用支付宝公钥验签名
        try {
            verified = RSASignature.doCheck(verifyData, sign, PartnerConfig.RSA_ALIPAY_PUBLIC);
            logger.info("verified="+verified);
        } catch (Exception e) {
            e.printStackTrace();
        }
        PrintWriter out = response.getWriter();
        //验证签名通过
        if (verified) {
            //根据交易状态处理业务逻辑
            
//当交易状态成功,处理业务逻辑成功。回写success
                    logger.info("支付宝支付:保存数据库信息success");
                    out.print("success");
                }else{
                    out.print("fail");
                }
            }
                
            }else{
                out.print("fail");
            }
        } else {
            System.out.println("接收支付宝系统通知验证签名失败,请检查!");
            out.print("fail");
        }
        }catch (Exception e) {
            e.printStackTrace();
            logger.info(e);
        }
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        doPost(request, response);
    }

    /**
     * 获得验签名的数据
     * 
@param map
     * 
@return
     * 
@throws Exception 
     
*/
    @SuppressWarnings("unchecked")
    private String getVerifyData(Map map) {
        String notify_data = (String) ((Object[]) map.get("notify_data"))[0];
        logger.info(notify_data);
        return "notify_data="+notify_data;
    }
}
RSANotifyReceiver.java是支付宝回调服务器的servlet,需要安卓客户端传递该servlet地址及相关参数给支付宝,支付宝会在支付完成之后进行回调.回调之后会返回消息给支付宝.
/**
 * Alipay.com Inc.
 * Copyright (c) 2005-2008 All Rights Reserved.
 
*/
package com.alipay.client.trade;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.Security;

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.airplane.action.BackStageAction;
import com.alipay.client.base.PartnerConfig;
import com.alipay.client.security.RSASignature;
import com.alipay.client.util.StringUtil;

/**
 * 
 * 安全支付服务器端处理程序
 * 
 * 1.将业务参数:合作商户ID、 外部交易号、商品名称、商品的具体描述、商品总价、卖家帐户、notify_url这些参数按照固定顺序签名
 * 2.将签名结果返回客户端
 * 
@author 3Y
 
*/
public class RSATrade extends HttpServlet {

    private static final long serialVersionUID = -3035307235076650766L;
    private static Log logger = LogFactory.getLog(RSATrade.class);
    static {
          Security.addProvider(new BouncyCastleProvider());
       }
    String basePath="";
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        System.out.println("request in");
        response.setCharacterEncoding("utf-8");
        PrintWriter out = response.getWriter();
        logger.info("here");
        //得到应用服务器地址
        String path = request.getContextPath();
        basePath = request.getScheme() + "://" + request.getLocalAddr() + ":"
                                   + request.getServerPort() + path + "/";
        logger.info("here1");
        String strReString="";
        //检查商户PartnerConfig.java文件的配置参数
        logger.info(checkInfo());
        if(!checkInfo()){
            strReString="<result><is_success>F</is_success><error>缺少partner或者seller," +
                    "请在com/alipay/client/base/PartnerConfig.java中增加</error></result>";
            out.print(strReString);
            return;
        }
        
        String signData=getSignDate(request);
        String sign = sign(signData,PartnerConfig.RSA_PRIVATE);
        signData = URLEncoder.encode(signData,"utf-8");
         sign = URLEncoder.encode(sign,"utf-8");
        logger.info(signData);
        logger.info(sign);
        //返回待签名数据和签名数据
        strReString="<result><is_success>T</is_success><content>"+signData+"</content><sign>"+sign+"</sign></result>";
        logger.info(strReString);
        //对返回客户端的数据encode
        out.print(strReString); 
        return;
    }
    
    

    //检查商户PartnerConfig.java文件的配置参数
    private boolean checkInfo() {
        String partner = PartnerConfig.PARTNER;
        String seller = PartnerConfig.SELLER;
        //如果合作商户ID为空或者账号ID为空返回false
        if (StringUtil.isBlank(partner) || StringUtil.isBlank(seller))
            return false;
        
        return true;
    }
    
    
    /**
     * 准备待签名的数据
     * 
     * 
@param request
     * 
@return
     * 
@throws UnsupportedEncodingException
     
*/
    private String getSignDate(
            HttpServletRequest request) throws UnsupportedEncodingException {
        request.setCharacterEncoding("utf-8");
        
        //合作商户ID
        String partner = PartnerConfig.PARTNER;
        //卖家帐号
        String seller = PartnerConfig.SELLER;
     // 外部交易号 这里取当前时间,商户可根据自己的情况修改此参数,但保证唯一性
        String outTradeNo = System.currentTimeMillis() + "";
        String opt = request.getParameter("opt").trim();
        String user_id = request.getParameter("user_id").trim();
        //opt:0为支付,1为充值
        if("0".equals(opt)){
            outTradeNo = request.getParameter("order_id").trim();
        }else if("1".equals(opt)){
            outTradeNo = System.currentTimeMillis() + "";
        }
        
        // 商品名称
        String subject = request.getParameter("subject").trim();
        // 商品具体描述
        String body = request.getParameter("body").trim();
        // 商品总价
        String totalFee = request.getParameter("total_fee").trim();
        // 接收支付宝发送的通知的url 商户可根据自己的情况修改此参数
        String notify_url = basePath+"servlet/RSANotifyReceiver?opt=" + opt +"&order_id="+outTradeNo+"&user_id="+user_id+"&payMoney="+totalFee;
        
        //组装待签名数据
        String signData = "partner=" + "\"" + partner + "\"";
        signData += "&";
        signData += "seller=" + "\"" + seller + "\"";
        signData += "&";
        signData += "out_trade_no=" + "\"" + outTradeNo + "\"";
        signData += "&";
        signData += "subject=" + "\"" + subject+ "\"";
        signData += "&";
        signData += "body=" + "\"" + body + "\"";
        signData += "&";
        signData += "total_fee=" + "\""+ totalFee + "\"";
        signData += "&";
        signData += "notify_url=" + "\""+notify_url+ "\"";
        return signData;
    }

    
    
    /**
     * 对参数进行签名
     * 
     * 
@param signData 待签名数据,key rsa商户私钥
     * 
@return
     
*/
    private String sign(String signData,String key) {
        System.out.println("signData:"+signData);
        String sign = "";
        try {
            sign = RSASignature.sign(signData, key);
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        return sign;
    }

    
}
RSATrade.java为客户端与服务器端为商品信息进行的签名操作,签名完成之后服务器会将签名信息以及回调地址返给客户端.客户端拿到回调地址交给支付宝进行回调.
支付宝流程为:客户端在支付宝支付之前请求RSATrade.java,此时服务器为商品信息注册签名,签名完成之后服务器把签名过的商品信息和支付宝的回调地址(RSANotifyReceiver
)和参数传给客户端,客户端会把回调地址和签过名的商品信息交给支付宝,支付宝在完成支付之后请求回调地址,进行签名和公钥验证,验证成功即可执行持久化操作.若操作成功,客户端会请求RSACallBack.java类,进行客户端与服务器端的同步操作.
posted on 2013-08-14 14:12 龚小伟 阅读(8522) 评论(5)  编辑  收藏 所属分类: 支付

评论

# re: 支付宝快捷支付(安卓版)的服务器开发流程 2013-12-26 22:58 迷途小书童
求教:
对于// 商户(RSA)私钥
public static final String RSA_PRIVATE = "";
这项是放在服务器端工作?
不能放在手机客户端吗?

商户密钥泄露会导致什么问题?  回复  更多评论
  

# re: 支付宝快捷支付(安卓版)的服务器开发流程 2013-12-27 14:15 gxw
@迷途小书童
商户私钥可以放在客户端.支付宝是通过商户私钥生产商户公钥,通过商户公钥上传给支付宝服务器,生产支付宝公钥,给商品签名的时候拿着商户私钥和支付宝公钥进行签名以及后续的验签,即使盗用商户私钥,也不会产生任何安全问题  回复  更多评论
  

# re: 支付宝快捷支付(安卓版)的服务器开发流程 2015-08-09 11:00 steaming
如何得到订单号对应的商品信息 只有订单号 价格 状态不行吧 不知道买的什么 是将订单号设置为商品id吗 还是别的方式  回复  更多评论
  

# re: 支付宝快捷支付(安卓版)的服务器开发流程 2015-08-27 09:55 龚小伟
@steaming
商品信息应该有对应的商品id 你可以把商品id放到支付宝的回调地址中。即把商品id拼接到notify_url这个参数  回复  更多评论
  

# re: 支付宝快捷支付(安卓版)的服务器开发流程 2015-09-29 19:46 zzapplet
是否支持并发不在于sql怎么写,而在于你的事物控制:
例如:spring中的
@Transactional(rollbackFor = Throwable.class, isolation = Isolation.SERIALIZABLE)

写代码不仅仅要面向对象,而且要有分层的思想。  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: