名词定义
- 用户:在我们网站上交易的客户。
- 支付网关:是我们商户和PayPal对接的系统。
- 商户系统:充值成功后用于发货的系统。
- PayPal:是海外的一家支付平台。
PayPal官网地址
官网:https://paypal.com
开发者网站:https://developer.paypal.com
沙盒地址:https://www.sandbox.paypal.com/
Demo地址:https://demo.paypal.com
序列图
用户登录我们的网站,选择好商品之后,点击使用PayPal付款,直到用户户看到付款成功。对应的序列图如下:
我们就是要做上图这么一件事,下面是详细的开发步骤。
一、注册个人账号
注册地址:https://www.paypal.com/us/webapps/mpp/account-selection
选择个人账户
即可。
只需按部就班填写即可,此处不再截图注册流程。
二、创建测试账号
开发者网站:https://developer.paypal.com/
用上面注册的账户登录开发者网站,在工作台上创建一个商户账号和一个买家账号,用于开发和测试。
在创建买家账户的时候,别忘了给买家账户里多加一些钱。
三、获取clientId和secret
在调用REST API时要在请求头中加入token,而token是用clientId和secret获取的。
四、获取token
1 | import com.alibaba.fastjson.JSON; |
其中tokenUrl为:https://api.sandbox.paypal.com/v1/oauth2/token
对应接口文档地址:https://developer.paypal.com/docs/api/overview/#make-your-first-call
注意:token具有时效性。
五、预交易(下单)
这是一个预交易接口,即调用后不会真正的发生金钱交易,只是告诉PayPal有个用户一会要支付1000元买手机,你给我生成一个支付链接,我把这个链接给用户,让他去到你网站支付。调用此接口后会返回支付链接和查询此交易的查询链接。
1 | /** |
PaymentRequest
是一个实体类,和接口文档中的请求json结构一致。returnUrl
是用户确认付支付后,PayPal向我们商户跳转的连接,PayPal会带有一些参数,同时我们也可以把我们的流水号放进去。cancelUrl
是用户在paypal界面点击取消付款后,paypal想我们商户跳转的连接。
HttpUtil.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67import org.apache.commons.lang3.StringUtils;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.Map;
/**
* @author kangyonggan
* @since 10/24/18
*/
public final class HttpUtil {
private HttpUtil() {
}
/**
* 发送http请求
*
* @param url
* @param header
* @param body
* @return
* @throws Exception
*/
public static String send(String url, Map<String, String> header, String body) throws Exception {
StringBuilder result = new StringBuilder();
PrintWriter out = null;
BufferedReader in = null;
try {
URL realUrl = new URL(url);
URLConnection conn = realUrl.openConnection();
if (header != null) {
for (String key : header.keySet()) {
conn.setRequestProperty(key, header.get(key));
}
}
conn.setDoOutput(true);
conn.setDoInput(true);
if (StringUtils.isNotEmpty(body)) {
out = new PrintWriter(conn.getOutputStream());
out.print(body);
out.flush();
}
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result.append(line).append("\n");
}
return result.toString();
} catch (Exception e) {
throw e;
} finally {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
}
}
}
到此,用户已经拿到了付款连接,可以跳转到paypal进行付款了。
对应接口文档地址:https://developer.paypal.com/docs/api/payments/v1/#payment_create
六、执行付款
当用户在paypal界面完成付款后,paypal就会重定向到我们送给他的returnUrl
:
1 | /** |
其中executeUrl为:https://api.sandbox.paypal.com/v1/payments/payment
注意:不能在此方法内调用商户系统进行发货,因为这个请求可能不是paypal发来的,我们应该是后台通知时再通知商户系统发货。
对应的接口文档地址:https://developer.paypal.com/docs/api/payments/v1/#payment_execute
七、后台通知
可以使用卖家账户登录沙盒环境,点击”卖家习惯设定“ > “收款和管理我的风险” > “即时付款通知”。
直达链接:https://www.sandbox.paypal.com/c2/cgi-bin/webscr?cmd=_profile-display-handler&tab_id=SELLER_PREFERENCES
1 | /** |
其中checkUrl:https://www.sandbox.paypal.com/cgi-bin/webscr
对应接口文档地址:https://www.paypal.com/us/cgi-bin/webscr?cmd=p/acc/ipn-info-outside
八、单笔查询
如果我们没收到后台通知怎么办?这时候就可以通过单笔查询主动去paypal查询订单状态。
1 | Map<String, String> header = new HashMap<>(8); |
其中queryUrl:https://api.sandbox.paypal.com/v1/payments/payment
paymentId是在预交易接口返回的。
对应接口文档地址:https://developer.paypal.com/docs/api/payments/v1/#payment_get
九、批量查询
如果我们需要日终对账,那么就要用到批量查询接口。但是此接口不大符合我的需要,因此我没用这个接口,而是循环使用单笔查询代替的。
对应接口文档地址:https://developer.paypal.com/docs/api/payments/v1/#payment_list