非SDK支付方式,因为paypal 支付的SDK 说是要大客户申请权限了 才让使用,简直恶心,之前不知道,测试都弄完了,切换正式环境,提示权限不足,问客服才知道,简直头疼,经测试以及可以正常使用了的 ,使用的方式为:api 签名方式进行,支付采用的是平行支付,支持同时向多人发起付款或者单人付款(应用于平台,商家,客户这种场景,一次付款,多方收账)

支付一共分三部分  第一部分: 获取token,获取前端专递过来的金额  然后拼接对应格式数据,获取到token,第二部:然后重定向到paypal  这里会跳转到一个网页,用户会看到商品信息以及金额 然后 用户授权支付 第三部分:用户授权后 拿到token 进行最后的确认支付操作

第一、第二部分:

package com.chanxa.monitor.web.recharge;

import java.math.BigDecimal;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.codec.binary.Base64;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.chanxa.monitor.pay.service.PaypalInterface;

@Controller
@RequestMapping("/recharge")
public class RechargeController {

    @Resource
    private PaypalInterface paypalService;
    
    public Map<String, String> getParamsFromRequest(HttpServletRequest request) {
        Map<String, String> params = new HashMap<String, String>();
        Map<String, String[]> requestParams = request.getParameterMap();
        for (Iterator<String> 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] + ",";
            }
            // 乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
            params.put(name, valueStr);
        }
        return params;
    }
    
    /**
     * 充值接口
     */
    @RequestMapping("/RechargePaypal.do")
    public String RechargePaypal(HttpServletRequest request){
        //使用RSA 加密了数据  网上有生成公钥私钥的方法 自己拿一套来生成对应的公钥私钥使用就好了
        String privateKey="";//私钥
        //获取对应的参数
        String userId=request.getParameter("userId");
        String accountId=request.getParameter("accountId");
        String money=request.getParameter("money");
        //解析出正确的数据
        try {
            userId=decrypt(userId,privateKey);
            accountId=decrypt(accountId,privateKey);
            money=decrypt(money,privateKey);
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
        //一系列参数验证  略...
        
        //调用paypal支付
        String token=paypalService.getPaypalPage(new BigDecimal(money),"测试充值");
        if(token.indexOf("redirect")!=-1){
            return token;
        }
        //增加数据充值记录 略。。。
        
        //重定向到paypal支付页面
        String paypalLog="https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=";//测试环境,正式的为去掉sandbox
        return "redirect:"+paypalLog+token;
    }
    
    /** 
     * RSA私钥解密
     *  
     * @param str 
     *            加密字符串
     * @param privateKey 
     *            私钥 
     * @return 铭文
     * @throws Exception 
     *             解密过程中的异常信息 
     */  
    public  String decrypt(String str, String privateKey) throws Exception{
        //64位解码加密后的字符串
        byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
        //base64编码的私钥
        byte[] decoded = Base64.decodeBase64(privateKey);  
        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));  
        //RSA解密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, priKey);
        String outStr = new String(cipher.doFinal(inputByte),"UTF-8");
        return outStr;
    }

}

 

package com.chanxa.monitor.pay.service;

import java.math.BigDecimal;



public interface PaypalInterface {
    /**
     *打开支付页面
     */
    public  String getPaypalPage(BigDecimal money,String name);
}
package com.chanxa.monitor.pay.service.impl;
import java.math.BigDecimal;
import java.net.URLDecoder;
import javax.annotation.Resource;

import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.chanxa.monitor.pay.paypal.config.PaypalConfig;
import com.chanxa.monitor.pay.service.PaypalInterface;
import com.chanxa.monitor.service.AccountInterface;
import com.chanxa.monitor.utils.Configuration;
@Service
public class PaypalService implements PaypalInterface{
    Logger logger = LoggerFactory.getLogger(PaypalService.class);
    @Resource
    private AccountInterface accountService;
    @Override
    public String getPaypalPage( BigDecimal money, String name) {
        try {
            PostMethod postMethod = null;
            //组装数据
            postMethod=    this.getPostMethod(money, name,"78","SetExpressCheckout");
            //模拟post请求 
            org.apache.commons.httpclient.HttpClient httpClient = new org.apache.commons.httpclient.HttpClient();
            int response = httpClient.executeMethod(postMethod); // 执行POST方法
            String result = postMethod.getResponseBodyAsString() ;
            //解析返回值
            result=URLDecoder.decode(result,"UTF-8");
            String [] results=result.split("&");
            String token=results[0];
            if(token.indexOf("TOKEN")==-1){
                 for(int i=0;i<results.length;i++){
                     String str=results[i];
                     String [] strs=str.split("=");
                     if(strs[0].equals("PAYMENTINFO_0_ERRORCODE")){//余额不足
                         if(strs[0].equals("10009")){
                             //这里是失败了返回自己自定义页面
                             return PaypalConfig.payPalSuccess+"?code=3"; 
                         }
                     }
                 }
            }
            String [] tokens=token.split("=");
             token=tokens[1];
             //返回token
             return token;
        } catch (Exception e) {
            logger.info("请求异常"+e.getMessage(),e);
            throw new RuntimeException(e.getMessage());
        }
    }
    /**
     * 
     * @param money 金额
     * @param name 标题
     * @param version 版本
     * @param method 固定为SetExpressCheckout
     * @return
     */
    public PostMethod getPostMethod(BigDecimal money,String name,String version,String method){
        try {
            PostMethod postMethod = null;
            postMethod = new PostMethod("https://api-3t.sandbox.paypal.com/nvp") ;//paypal 支付请求网关 --sandbox 为测试地址
            postMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8") ;
                //判断 是否为禁用分账 1:是 2:否
                String isFenzhang=Configuration.getConfig().getValue("isFenzhang");
                if(isFenzhang.equals("1")){
                         NameValuePair[] data = {
                                   new NameValuePair("USER",""),//  api用户名 官网可以查到该API名
                                   new NameValuePair("PWD",""),//  api密码 和API用户名一起的
                                   new NameValuePair("SIGNATURE",""),//签名 paypal  签名   该签名可以在官网找到  
                                   new NameValuePair("METHOD",method),//请求接口名
                                   new NameValuePair("VERSION",version),//版本号
                                   new NameValuePair("PAYMENTREQUEST_0_PAYMENTACTION","SALE"),
                                   new NameValuePair("PAYMENTREQUEST_0_AMT",""),//交易金额
                                   new NameValuePair("PAYMENTREQUEST_0_CURRENCYCODE","HKD"),//交易币种
                                   new NameValuePair("PAYMENTREQUEST_0_SELLERPAYPALACCOUNTID",PaypalConfig.Receivables),//平台账号
                                   new NameValuePair("PAYMENTREQUEST_0_ITEMAMT",""),//与交易金额相同即可
                                   new NameValuePair("PAYMENTREQUEST_0_PAYMENTREQUESTID","CART26488-PAYMENT0"),//付款请求ID
                                   new NameValuePair("L_PAYMENTREQUEST_0_NAME0",name),//交易名称
                                   new NameValuePair("L_PAYMENTREQUEST_0_QTY0","1"),
                                   new NameValuePair("L_PAYMENTREQUEST_0_AMT0",""),//与交易金额相同即可
                                 
                                   new NameValuePair("PAYMENTREQUEST_1_PAYMENTACTION","SALE"),
                                   new NameValuePair("PAYMENTREQUEST_1_AMT",""),//交易金额
                                   new NameValuePair("PAYMENTREQUEST_1_CURRENCYCODE","HKD"),
                                   new NameValuePair("PAYMENTREQUEST_1_SELLERPAYPALACCOUNTID",""),//运营商账号
                                   new NameValuePair("PAYMENTREQUEST_1_ITEMAMT",""),//交易金额
                                   new NameValuePair("PAYMENTREQUEST_1_PAYMENTREQUESTID","CART26488-PAYMENT1"),
                                   new NameValuePair("L_PAYMENTREQUEST_1_NAME0",name),
                                   new NameValuePair("L_PAYMENTREQUEST_1_QTY0","1"),
                                   new NameValuePair("L_PAYMENTREQUEST_1_AMT0",""),//交易金额
                                   //....如果有多方  还能继续添加  最多好像是20个来着还是多少
                                   new NameValuePair("cancelUrl",PaypalConfig.cancelUrl),//支付回跳页面
                                   new NameValuePair("returnUrl",PaypalConfig.returnUrl)//支付回调地址--充值
                                        
                                };
                                postMethod.setRequestBody(data);
                }else{
                     NameValuePair[] data = {
                               new NameValuePair("USER",""),//api用户名 官网可以查到该API名
                               new NameValuePair("PWD",""),//api密码 和API用户名一起的
                               new NameValuePair("SIGNATURE",""),//签名
                               new NameValuePair("METHOD",method),//请求接口名
                               new NameValuePair("VERSION",version),//版本号
                               new NameValuePair("PAYMENTREQUEST_0_PAYMENTACTION","SALE"),
                               new NameValuePair("PAYMENTREQUEST_0_AMT",""),//交易金额
                               new NameValuePair("PAYMENTREQUEST_0_CURRENCYCODE","HKD"),//交易币种
                               new NameValuePair("PAYMENTREQUEST_0_SELLERPAYPALACCOUNTID",""),//平台账号
                               new NameValuePair("PAYMENTREQUEST_0_ITEMAMT",""),
                               new NameValuePair("PAYMENTREQUEST_0_PAYMENTREQUESTID","CART26488-PAYMENT0"),//付款请求ID
                               new NameValuePair("L_PAYMENTREQUEST_0_NAME0",name),//交易名称
                               new NameValuePair("L_PAYMENTREQUEST_0_QTY0","1"),
                               new NameValuePair("L_PAYMENTREQUEST_0_AMT0",""),//金额
                               new NameValuePair("cancelUrl",PaypalConfig.cancelUrl),
                               new NameValuePair("returnUrl",PaypalConfig.returnUrl)
                            };
                     postMethod.setRequestBody(data);
                }
                return postMethod;
        } catch (Exception e) {
            logger.info("请求异常"+e.getMessage(),e);
            throw new RuntimeException(e.getMessage());
        }
        
    }
    
}

第三部分:最后在回调里进行确认付款操作,

注意:
因为充值是用的平行支付 会出现两个支付订单明细 所以这里不能已单一的一个返回就来确认是成功 (如果只配置了一个收款方 则不受影响 配置多个收款方时  就得判断每个收款方是否都成功了 ) 
 这里存在一个问题  如果是多个收款方 当其中某一方的收款账号是错误的时候,按照业务逻辑 这时应该要进行退款操作,
比如  我有5个收款方 用户A 进行付款 前面4个都交易成功,第五个因为账号不存在等问题,失败了 ,这时候按逻辑是要吧前面4个人的钱都退出来,
但是我和官方人员沟通,他们告诉我  是不能进行退款的  因为paypal退款是需要收款方授权,也就是说 这个时候并不能自主退款,得对接另一套协议才行
package com.chanxa.monitor.web.paypal;

import java.io.PrintWriter;
import java.net.URLDecoder;
import java.util.Map;

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

import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.chanxa.monitor.pay.paypal.config.PaypalConfig;
import com.chanxa.monitor.utils.Configuration;
import com.chanxa.monitor.web.recharge.RechargeController;


@Controller
@RequestMapping("/paypalhttp/")
public class PaypalHttp {
    Logger logger = LoggerFactory.getLogger(PaypalHttp.class);
    @Resource
    private RechargeController rechargeController;
    /**
     * 买家授权后的回调--充值
     */
    @RequestMapping(value = "/notify.do")
    public String notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        PrintWriter out = response.getWriter();
        // 获取POST过来反馈信息
        Map<String, String> params=rechargeController.getParamsFromRequest(request);
        //根据 {PayerID=WTTKS42LBXASQ, token=EC-7CF05834S75613304} 这两个值 再去发起付款
        String PayerID=params.get("PayerID");
        String token=params.get("token");
        String name="测试充值";
        try {
                PostMethod postMethod = null;
                postMethod = new PostMethod(PaypalConfig.pathUrl) ;
                postMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8") ;
              //判断 是否为禁用分账 1:是 2:否,
              //注意:   这里可以使用前面的组装数据的公共接口  只是需要加个参数  第一个是请求接口变动了(DoExpressCheckoutPayment) 然后是版本(204),还有就是返回过来的token=TOKEN 和交易ID=PAYERID  都要放进去 其余的没什么变化
                String isFenzhang=Configuration.getConfig().getValue("isFenzhang");
                if(isFenzhang.equals("1")){
                         NameValuePair[] data = {
                                   new NameValuePair("USER",""),//  api用户名 官网可以查到该API名
                                   new NameValuePair("PWD",""),//  api密码 和API用户名一起的
                                   new NameValuePair("SIGNATURE",""),//签名 paypal  签名   该签名可以在官网找到  
                                   new NameValuePair("METHOD","DoExpressCheckoutPayment"),//请求接口名
                                   new NameValuePair("VERSION","204"),//版本号
                                   new NameValuePair("PAYMENTREQUEST_0_PAYMENTACTION","SALE"),
                                   new NameValuePair("PAYMENTREQUEST_0_AMT",""),//交易金额
                                   new NameValuePair("PAYMENTREQUEST_0_CURRENCYCODE","HKD"),//交易币种
                                   new NameValuePair("PAYMENTREQUEST_0_SELLERPAYPALACCOUNTID",PaypalConfig.Receivables),//平台账号
                                   new NameValuePair("PAYMENTREQUEST_0_ITEMAMT",""),//与交易金额相同即可
                                   new NameValuePair("PAYMENTREQUEST_0_PAYMENTREQUESTID","CART26488-PAYMENT0"),//付款请求ID
                                   new NameValuePair("L_PAYMENTREQUEST_0_NAME0",name),//交易名称
                                   new NameValuePair("L_PAYMENTREQUEST_0_QTY0","1"),
                                   new NameValuePair("L_PAYMENTREQUEST_0_AMT0",""),//与交易金额相同即可
                                 
                                   new NameValuePair("PAYMENTREQUEST_1_PAYMENTACTION","SALE"),
                                   new NameValuePair("PAYMENTREQUEST_1_AMT",""),//交易金额
                                   new NameValuePair("PAYMENTREQUEST_1_CURRENCYCODE","HKD"),
                                   new NameValuePair("PAYMENTREQUEST_1_SELLERPAYPALACCOUNTID",""),//运营商账号
                                   new NameValuePair("PAYMENTREQUEST_1_ITEMAMT",""),//交易金额
                                   new NameValuePair("PAYMENTREQUEST_1_PAYMENTREQUESTID","CART26488-PAYMENT1"),
                                   new NameValuePair("L_PAYMENTREQUEST_1_NAME0",name),
                                   new NameValuePair("L_PAYMENTREQUEST_1_QTY0","1"),
                                   new NameValuePair("L_PAYMENTREQUEST_1_AMT0",""),//交易金额
                                   new NameValuePair("TOKEN",token),
                                   new NameValuePair("PAYERID",PayerID),
                                   //....如果有多方  还能继续添加  最多好像是20个来着还是多少
                                   new NameValuePair("cancelUrl",PaypalConfig.cancelUrl),//支付回跳页面
                                   new NameValuePair("returnUrl",PaypalConfig.returnUrl)//支付回调地址--充值
                                        
                                };
                                postMethod.setRequestBody(data);
                }else{
                     NameValuePair[] data = {
                               new NameValuePair("USER",""),//api用户名 官网可以查到该API名
                               new NameValuePair("PWD",""),//api密码 和API用户名一起的
                               new NameValuePair("SIGNATURE",""),//签名
                               new NameValuePair("METHOD","DoExpressCheckoutPayment"),//请求接口名
                               new NameValuePair("VERSION","204"),//版本号
                               new NameValuePair("PAYMENTREQUEST_0_PAYMENTACTION","SALE"),
                               new NameValuePair("PAYMENTREQUEST_0_AMT",""),//交易金额
                               new NameValuePair("PAYMENTREQUEST_0_CURRENCYCODE","HKD"),//交易币种
                               new NameValuePair("PAYMENTREQUEST_0_SELLERPAYPALACCOUNTID",""),//平台账号
                               new NameValuePair("PAYMENTREQUEST_0_ITEMAMT",""),
                               new NameValuePair("PAYMENTREQUEST_0_PAYMENTREQUESTID","CART26488-PAYMENT0"),//付款请求ID
                               new NameValuePair("L_PAYMENTREQUEST_0_NAME0",name),//交易名称
                               new NameValuePair("L_PAYMENTREQUEST_0_QTY0","1"),
                               new NameValuePair("L_PAYMENTREQUEST_0_AMT0",""),//金额
                               new NameValuePair("TOKEN",token),
                               new NameValuePair("PAYERID",PayerID),
                               new NameValuePair("cancelUrl",PaypalConfig.cancelUrl),
                               new NameValuePair("returnUrl",PaypalConfig.returnUrl)
                            };
                     postMethod.setRequestBody(data);
                }
               
                org.apache.commons.httpclient.HttpClient httpClient = new org.apache.commons.httpclient.HttpClient();
                int status = httpClient.executeMethod(postMethod); // 执行POST方法
                String result = postMethod.getResponseBodyAsString() ;
                result=URLDecoder.decode(result,"UTF-8");
                //数据解析
                this.setPaypal(result, 2, token);
                return PaypalConfig.payPalSuccess+"?code=1";
            } catch (Exception e) {
                logger.info("请求异常"+e.getMessage(),e);
//                throw new RuntimeException(e.getMessage());
                return PaypalConfig.payPalSuccess+"?code=4";
            }
//        out.flush();
//        out.close();
        
    }
    /**
     * /数据解析 公共方法
     * @param result 返回字符串
     * @param type 类型区分  2:充值 1:缴纳管理费
     */
    public void setPaypal(String result,Integer type,String token){
         String [] results=result.split("&");
            //固定模式  充值时 会返回1+n组数据  这个N是指前面配置了多少个交易对象 这里 用循环来处理
            for(int k=0;k<type;k++){
                 for(int i=0;i<results.length;i++){
                        String str=results[i];
                        String [] strs=str.split("=");
                        if(strs[0].equals("ACK")){
                            if(strs[1].equals("Success")){//交易成功
                                
                            }
                        }
                        if(strs[0].equals("PAYMENTINFO_"+k+"_FEEAMT")){//交易手续费
                            
                        }
                        if(strs[0].equals("PAYMENTINFO_"+k+"_AMT")){//设置金额
                            
                        }
                        if(strs[0].equals("PAYMENTINFO_"+k+"_SELLERPAYPALACCOUNTID")){//收款方账号
                            
                        }
                        if(strs[0].equals("PAYMENTINFO_"+k+"_CURRENCYCODE")){//币种
                            
                        }
                        if(strs[0].equals("PAYMENTINFO_"+k+"_ORDERTIME")){//支付时间
                            
                        }
                        if(strs[0].equals("PAYMENTINFO_"+k+"_TRANSACTIONID")){//交易流水号
                            
                        }
                        if(strs[0].equals("TOKEN")){//订单ID
                            
                        }
                        if(strs[0].equals("PAYMENTINFO_"+k+"_TRANSACTIONTYPE")){//交易类型
                            
                        }
                        if(strs[0].equals("PAYMENTINFO_"+k+"_ACK")){//交易状态
                            
                            if(strs[1].equals("Success")){//交易状态为成功  则进行对应的操作
                                    /**
                                     *因为充值是用的平行支付 会出现两个支付订单明细 所以这里不能已单一的一个返回就来确认是成功 (如果只配置了一个收款方 则不受影响 配置多个收款方时  就得判断每个收款方是否都成功了 )
                                     * 注意 这里存在一个问题  如果是多个收款方 当其中某一方的收款账号是错误的时候,按照业务逻辑 这时应该要进行退款操作,
                                     * 比如  我有5个收款方 用户A 进行付款 前面4个都交易成功,第五个因为账号不存在等问题,失败了 ,这时候按逻辑是要吧前面4个人的钱都退出来,
                                     * 但是我和官方人员沟通,他们告诉我  是不能进行退款的  因为paypal退款是需要收款方授权,也就是说 这个时候并不能自主退款,得对接另一套协议才行
                                     */
                                }
                            
                        }
            }
                 //进行日志记录
                    
            }
            
                //执行对应的充值成功逻辑
            
           
    }
}

好了 收工