paypal支付--平行支付
Posted thirty-30
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了paypal支付--平行支付相关的知识,希望对你有一定的参考价值。
非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退款是需要收款方授权,也就是说 这个时候并不能自主退款,得对接另一套协议才行 */ } } } //进行日志记录 } //执行对应的充值成功逻辑 } }
好了 收工
以上是关于paypal支付--平行支付的主要内容,如果未能解决你的问题,请参考以下文章