由于公司需求,需做国际支付,首先网上查找资料,最终选定paypal的快捷支付集成到我们的网站。
1.注册一个paypal账户,待激活后,进入paypal页面,然后选择商家工具,你会发现paypal对于商家共有3大解决方案:网站付款标准版、点子邮件付款、paypal快速结账。
2.网站付款标准版:此种方式是最简单将支付功能集成到网站,其总共3个步骤
a.创建付款按钮。
b.在您的网站添加按钮。
c.创建按钮后注册PayPal账户。
3.电子邮件付款:此种方式需要卖家参与进来,直接就降低了网站购物的体验
4.PayPal快速结账:此种方式是paypal推荐的最佳集成网站付款解决方案
官方给出的这几种方案的区别
5.下载相关技术文档(中文):https://www.paypal-biz.com/developer/documentation/2135.html
下载示例代码(java):https://www.paypal-biz.com/developer/samplecode/2137.html
6.开始快捷支付:根据文档PayPal_Sandbox_Guide_CN_V2.0.pdf
a.创建主账号(已有)
b.登陆主账号,创建买家账号
c.创建卖家账号(创建详细过程请参见文档)
d.登陆卖家测试账号(https://www.sandbox.paypal.com)
e.申请API,如下图,paypal更推荐第二种(使用API证书),最后下载证书
f.加密刚下载的证书(可参见文档:https://developer.paypal.com/docs/classic/api/apiCredentials/)
openssl pkcs12 -export -in cert_key_pem.txt -inkey cert_key_pem.txt -out paypal_cert.p12
输入密码和确认密码(记住密码,后面要用,我在这里就犯了一次错误,抛出使用paypal的第一个异常)
g.解压下载后的示例代码,最好导入eclipse,看看其流程,将示例代码中的所有java文件拷贝至我的项目,并引入依赖jar包
maven依赖,我在这上面就吃过一次亏,抛出使用paypal的第二个异常,其中 xerces不是必须的显示依赖,开始我就没有导入
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.bluestemsoftware.open.maven.tparty</groupId>
<artifactId>xerces-impl</artifactId>
<version>2.9.0</version>
</dependency>
h.创建ExpressCheckOutController.java(项目使用springmvc),在其中创建方法接受用户的购买请求(具体可参考java示例代码的Login.jsp和ReviewOrder.jsp),我的测试代码如下:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
import com.paypal.sdk.core.nvp.NVPDecoder;
import com.paypal.sdk.core.nvp.NVPEncoder;
import com.paypal.sdk.profiles.APIProfile;
import com.paypal.sdk.profiles.ProfileFactory;
import com.paypal.sdk.services.NVPCallerServices;
@Controller("expressCheckOutController")
public class ExpressCheckOutController extends MultiActionController{
private static final String PRODUCT_NAME = "L_PAYMENTREQUEST_0_NAMEm";
private static final String PRODUCT_AMT = "L_PAYMENTREQUEST_0_AMTm";
private static final String PRODUCT_QTY = "L_PAYMENTREQUEST_0_QTYm";
@RequestMapping("checkout")
public void checkOut(HttpServletResponse response, HttpServletRequest request, HttpSession session) throws Exception{
NVPCallerServices caller = new NVPCallerServices();
APIProfile profile = ProfileFactory.createSSLAPIProfile();
profile.setAPIUsername("xxx"); //API用户名
profile.setAPIPassword("xxx"); //API密码
profile.setPrivateKeyPassword("xxx"); //加密证书时输入的密码(用于打开证书)
profile.setCertificateFile(session.getServletContext().getRealPath("WEB-INF/cert/paypal_cert.p12"));//证书位置
profile.setEnvironment("sandbox"); //表明为测试环境
caller.setAPIProfile(profile);
NVPEncoder encoder = new NVPEncoder();
encoder.add("METHOD","SetExpressCheckout");
String returnURL = "xxxxx/getexpresscheckoutdetails.do";
String cancelURL = "xxxx/cart.jsp";
// 商品总价
encoder.add("CANCELURL",cancelURL);
encoder.add("RETURNURL",returnURL);
encoder.add("PAYMENTREQUEST_0_PAYMENTACTION","sale");
encoder.add("PAYMENTREQUEST_0_AMT","23.00"); //总费用(邮费、保费、订单价格)
encoder.add("PAYMENTREQUEST_0_ITEMAMT","15.00"); //订单所有物品的价格
encoder.add("PAYMENTREQUEST_0_TAXAMT","5"); //订单中所有物品税费的总和
encoder.add("PAYMENTREQUEST_0_SHIPPINGAMT","1"); //邮费
encoder.add("PAYMENTREQUEST_0_HANDLINGAMT","1.00"); //订单处理费用的总额
encoder.add("PAYMENTREQUEST_0_INSURANCEAMT","1.00"); //保费
encoder.add("L_PAYMENTREQUEST_0_NAME0","The name of product 1");//物品名称
encoder.add("L_PAYMENTREQUEST_0_NUMBER0","5543312"); //物品编号
encoder.add("L_PAYMENTREQUEST_0_DESC0","The description of product 1");//物品描述
encoder.add("L_PAYMENTREQUEST_0_AMT0","10.00"); //物品单价
encoder.add("L_PAYMENTREQUEST_0_QTY0","1"); //物品购买数量
encoder.add("L_PAYMENTREQUEST_0_NAME1","The name of product 2");
encoder.add("L_PAYMENTREQUEST_0_NUMBER1","4431234");
encoder.add("L_PAYMENTREQUEST_0_DESC1","The description of product 2");
encoder.add("L_PAYMENTREQUEST_0_AMT1","5.00");
encoder.add("L_PAYMENTREQUEST_0_QTY1","1");
encoder.add("ALLOWNOTE","1"); //允许客户给我们留言
encoder.add("BRANDNAME","xxxx Technology"); //PayPal 结账页面显示的商户的标签
String strNVPRequest = encoder.encode();
String ppresponse = (String) caller.call( strNVPRequest);
NVPDecoder resultValues = new NVPDecoder();
resultValues.decode(ppresponse);
String strAck = resultValues.get("ACK");
if(strAck !=null && !(strAck.equals("Success") || strAck.equals("SuccessWithWarning")))
{
session.setAttribute("response",resultValues);
response.sendRedirect("/xxx/error/APIError.jsp");
return;
}else {
System.out.println(resultValues.get("TOKEN"));
response.sendRedirect("https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token="+resultValues.get("TOKEN"));
}
}
}
创建一个页面,然后请求上述controller中的方法,如果请求成功则打印TOKEN:EC-7UE75226S6032034V
到这里基本就算整合成功了吧,用户付款以及填写收获地址(这里为了简化,直接使用用户paypal中的收获地址)
付款成功后会调用下面指定的地址(由于是本地测试,所以无法回调到我的测试)
encoder.add("RETURNURL",returnURL);
在上面的url请求中主要获取用户的收获地址等重要信息,然后重定向到页面,显示用户购买成功,交易号,收获地址等
7.下面来谈谈我在编码时抛出的异常吧
a.未设置打开证书的密码,异常为:
严重: Servlet.service() for servlet [MVC] in context with path [/jaalee] threw exception [Request processing failed; nested exception is com.paypal.sdk.exceptions.FatalException: Could not read the API certificate file. Check the certificate password] with root cause
javax.crypto.BadPaddingException: Given final block not properly padded
添加如下设置即可解决
profile.setPrivateKeyPassword("加密证书时的密码");
b.未导入xerces 包,异常为:
org.xml.sax.SAXException: SAX2 driver class org.apache.xerces.parsers.SAXParser not found
java.lang.ClassNotFoundException: org.apache.xerces.parsers.SAXParser
java.lang.NullPointerException
at com.paypal.sdk.core.APICallerBase.getEndpointUrl(APICallerBase.java:289)
导入xerces包即可解决
8.请求的相关参数可参见:PayPal_EC_NVP_Guide_CN_V2.0.pdf 有详细介绍,记得把示例代码中的APIError.jsp文件拷贝至项目,如果参数有问题,直接定向至APIError.jsp,它会友好的显示错误