提醒一下,题主是在快速标准支付做到一半的时候换成了标准支付,所以该文档的快速支付大家做个参考就可以了.
一、两种支付方式
标准支付
优点:纯前端对接,简单方便,适用于非技术开发人员.个人即可用,不用花一个月时间进行商家认证(需要营业执照)
缺点:对接简单也就意味着无法执行复杂的工作,无法进行回调(因为本身就是给非技术开发人员使用的,如果这里有人发现可以回调请记得回来打我的脸!)
使用
快速支付
优点:可以进行回调,对接较为复杂的业务
缺点:开发难度大,需要相关的营业资质
二、沙盒使用
地址:https://developer.paypal.com/developer/applications/
为什么要使用沙盒环境:
快速支付的开通需要对应有国内相关的营业执照,但是通过沙盒环境我们可以直接获得一个仅在沙盒环境下可用的商家app
paypal作为国外的支付手段是无法直接使用国内的银行卡支付的,需要使用visa等卡
使用:
创建相关账号
点击Sandbox -> account
,进入主页的account下,分别创建一个商家账号和买家账号
(标准支付)登陆https://www.sandbox.paypal.com/,创建按钮
(快速支付)点击Dashboard -> My Account
,以商家账号为基础创建一个app,在开发快速支付时使用
三、标准支付
创建账号
创建相关的按钮,即生成对应的html代码
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="D7U8ME8UVW5MG">
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_buynow_LG.gif" border="0" name="submit" alt="PayPal——最安全便捷的在线支付方式!">
<img alt="" border="0" src="https://www.paypalobjects.com/zh_XC/i/scr/pixel.gif" width="1" height="1">
</form>
将代码嵌入到对应的页面即可(这里不放相关的代码了,因为都是和业务深度内嵌的)
补充:
按钮的代码可以定制不同的规格选项
看清楚按钮的本质:将相关的cmd,hosted_button_id等参数以post形式传送到https://www.paypal.com/cgi-bin/webscr,返回一个对应的支付页面
如果按钮的排版布局无法满足你的要求,比如你需要将不同的支付手段(如微信支付)集成到同一个按钮,可以设置一个能选择不同支付手段的按钮选项,根据这个选项间接地用js调用paypal支付按钮
paypal不支持国内的支付手段,支付需要使用VISA
四、快速支付
注:
本代码主要参考了这篇博客.遗憾的是,这篇博客距今已经有三年历史了,所以其实本人还使用了其它的博客进行参考,由于篇幅所限就不一一列举了.
由于本人没有visa的卡+该方案最终没有被采纳(因为相关资质的审核需要30天),实际本代码的可用性仅到订单生成为止,还无法确定回调是否可行
创建相关账号
获得app(这里具体的地址应该和沙盒一样,遗憾的是我个人并没有实际使用过)
sdk下载:
官方下载地址:https://developer.paypal.com/docs/nvp-soap-api/nvpsoap-sdks/
maven仓库:https://mvnrepository.com/search?q=paypal
开发
public Payment createPayment(Map<String,String> paraMap) throws PayPalRESTException {
Transaction transaction = new Transaction();
transaction.setDescription("icwlRegister");
// 将我们的订单ID保存到支付信息中,用于后面支付回传
if (null != paraMap.get("user_id")) {
transaction.setCustom(paraMap.get("user_id"));
}
/**
* 订单价格
* 注意两个公式:
* 1. total = subtotal(商品总额) + tax(税) + shopping(邮费) + other
* 2. subtotal = product1*num1+...
* 注意:如果有优化活动的话要坑
*/
Amount amount = new Amount();
// 设置各种费用
amount.setCurrency("USD"); //其实在paypal的返回值有说什么是哪个类哪个字段的问题
// 商品总价
amount.setTotal(paraMap.get("order_amount"));
Details details = new Details();
details.setSubtotal(paraMap.get("order_amount"));
// 税费
details.setTax("0");
amount.setDetails(details);
transaction.setAmount(amount);
ItemList itemList = new ItemList();
//收获地址不设置
/**
* 商品明细
*/
List<Item> items = new ArrayList<>();
Item item = new Item();
item.setSku("PRODUCT0000001");
item.setName("register");
item.setPrice(paraMap.get("order_amount"));
item.setQuantity("1");
item.setCurrency("USD"); //货币种类,注意应该不支持人民币
items.add(item);
itemList.setItems(items);
transaction.setItemList(itemList);
List<Transaction> transactions = new ArrayList<>();
transactions.add(transaction);
/**
* 支付信息
*/
Payer payer = new Payer();
payer.setPaymentMethod(PaypalPaymentMethod.paypal.toString());
Payment payment = new Payment();
payment.setIntent(PaypalPaymentIntent.sale.toString());
payment.setPayer(payer);
payment.setTransactions(transactions);
/**
* 回调地址
*/
RedirectUrls redirectUrls = new RedirectUrls();
redirectUrls.setReturnUrl("http://..."); //成功支付后的回调地址
redirectUrls.setCancelUrl("www.baidu.com"); //支付失败后的回调地址
payment.setRedirectUrls(redirectUrls);
System.out.println("调用之前:" + payment);
// 创建paypal API对象
APIContext apiContext = new APIContext(paypalConfigBean.getClientId(), paypalConfigBean.getClientSecret(), paypalConfigBean.getMode());
return payment.create(apiContext);
}
@Override
public String paypalService(Map map) throws PayPalRESTException {
HashMap<String, Object> resMap = new HashMap<>();
//paypal
Payment payment = createPayment( map ); //订单成功的回调地址
System.out.println("调用之后:" + payment);
for(Links links : payment.getLinks()){
if(links.getRel().equals("approval_url")){
resMap.put("url",links.getHref());
}
}
//数据库操作
map.put("payment_id",payment.getId()); //获得paypal的唯一识别id
System.out.println(map.get("payment_id"));
Integer maxUserId = userMapper.getMaxUserId();
Integer id = maxUserId==null?1:maxUserId+1;
System.out.println("id:" +id);
map.put("user_id",id.toString());
userMapper.addUser(map);
resMap.put("id",id);
return JsonUtil.toJsonString(resMap);
}
@Override
public void paypalNotify(String paymentId) {
Integer userId = userMapper.getUserIdByPaymentId(paymentId);
if(userId != null){
String dateFormat = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
userMapper.updateUserById(userId,sdf.format(new Date()));
}else{
System.out.println("Wrong!");
}
}
@Override
public String getUserId(Map map) {
Integer maxUserId = userMapper.getMaxUserId();
Integer id = maxUserId==null?1:maxUserId+1;
map.put("user_id",id);
userMapper.addUser(map);
//结果集
Map<String, Object> resMap = new HashMap<String, Object>();
resMap.put("userId",id);
return JsonUtil.toJsonString(resMap);
}
注:
这里的代码参考意义更强一些,因为没有完工,很烂(留着给自己做个记录吧)
订单价格的两个公式(如果不正确也是会报错的):
1. total = subtotal(商品总额) + tax(税) + shopping(邮费) + other
2. subtotal = product1*num1+..
总结一下
paypal支付的对接过程在我看来是非常糟心的,因为缺少相关的中文文档(自己这方面的能力的确还不足)
相关的api设置还不够完善,曾经的微信支付的api也是乱七八糟的,但现在都有了比较统一的接口.就比如关于价格的严格规则,其实就可以做成api调用,即总价格由计算得出,当然可能paypal本身的考虑也可能是为了使开发者避免单价价格和税率,总价的不一致性,那么我只能说这波paypal是在大气层!