这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战
配置账户
- 注册商家账号,网址:www.paypal.com/
- 访问开发者网站,网址:developer.paypal.com/ ,并使用注册好的商家账号进行登录
- 创建应用,并选择类型为sandbox,并记录ClientId和Secret
- 配置商家和个人账户 点击左侧[SandBox下的Accounts]创建两个账户,一个商家账户,一个个人账户,商家账户会默认关联我们上一步创建的应用。
- 商家测试账户登录地址:www.sandbox.paypal.com/
添加PayPal SDK依赖
// https://mvnrepository.com/artifact/com.paypal.sdk/checkout-sdk
implementation group: 'com.paypal.sdk', name: 'checkout-sdk', version: '1.0.4'
配置单例请求客户端
代码如下:
@Component
@Slf4j
public class PayPalClient {
private static PayPalHttpClient client;
private PayPalClient() {}
public static PayPalHttpClient client() {
if (client != null) {
return client;
}
PayPalEnvironment environment = !ProjectConfig.IS_TEST ? new PayPalEnvironment.Live(PayPalConfig.CLIENT_ID, PayPalConfig.CLIENT_SECRET)
: new PayPalEnvironment.Sandbox(PayPalConfig.CLIENT_ID, PayPalConfig.CLIENT_SECRET);
client = new PayPalHttpClient(environment);
return client;
}
}
获取支付链接
根据内部订单信息,并请求创建订单接口可以获取到授权支付链接,核心代码如下:
private OrderRequest buildRequestBody(OrderRecord orderRecord) {
OrderRequest orderRequest = new OrderRequest();
orderRequest.checkoutPaymentIntent(PayPalConfig.INTENT);
ApplicationContext context = new ApplicationContext()
// 商家名称,展示在支付界面
.brandName(PayPalConfig.BRAND_NAME)
.landingPage(PayPalConfig.LANDING_PAGE)
.cancelUrl(domainUrl + "/pay/result") // 取消返回地址
.returnUrl(domainUrl + "/pay/result") // 支付成功返回地址
.userAction(PayPalConfig.USER_ACTION)
.shippingPreference(PayPalConfig.SHIPPING_PREFERENCE);
orderRequest.applicationContext(context);
List<PurchaseUnitRequest> purchaseUnitRequests = new ArrayList<PurchaseUnitRequest>();
PurchaseUnitRequest unitRequest = new PurchaseUnitRequest()
.customId(orderRecord.getId() + "")
// 发票标识
.invoiceId(orderRecord.getId() + "")
.amountWithBreakdown(
new AmountWithBreakdown()
.currencyCode("USD")
.value(NumberUtil.decimalFormat("###.00", orderRecord.getMoney()))
);
purchaseUnitRequests.add(unitRequest);
orderRequest.purchaseUnits(purchaseUnitRequests);
return orderRequest;
}
划扣资金
paypal的授权支付链接和支付宝,微信的有所不同,微信和支付宝当用户通过支付链接后,资金划扣就完成了,而paypal只是一个授权,授权后的订单还要通过结账接口去划扣资金。
public HttpResponse<Order> captureOrder(String orderId) throws IOException {
OrdersCaptureRequest request = new OrdersCaptureRequest(orderId);
request.requestBody(buildRequestBody());
//3. Call PayPal to capture an order
HttpResponse<Order> response = PayPalClient.client().execute(request);
//4. Save the capture ID to your database. Implement logic to save capture to your database for future reference.
System.out.println("Status Code: " + response.statusCode());
System.out.println("Status: " + response.result().status());
System.out.println("Order ID: " + response.result().id());
System.out.println("Links: ");
for (LinkDescription link : response.result().links()) {
System.out.println("\t" + link.rel() + ": " + link.href());
}
System.out.println("Capture ids:");
for (PurchaseUnit purchaseUnit : response.result().purchaseUnits()) {
for (Capture capture : purchaseUnit.payments().captures()) {
System.out.println("\t" + capture.id());
}
}
System.out.println("Buyer: ");
Payer buyer = response.result().payer();
System.out.println("\tEmail Address: " + buyer.email());
System.out.println("\tName: " + buyer.name().fullName());
return response;
}
缺点
聪明的读者可能已经发现了,这个版本的划扣资金过程是不牢靠的,因为是同步返回的,当网络不稳定时,可能会出现用户已经授权了,但资金并没有划扣的情况。针对这种情况,应该配置异步通知的方式来保证一定能接受到用户授权划扣的请求。
关于异步通知(IPN)的设置和处理,我们下个版本再谈。
代码地址
gitee仓库地址:gitee.com/lanrain/art…