对接paypal支付最新JAVA版

3,271 阅读2分钟

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

配置账户

  • 注册商家账号,网址:www.paypal.com/
  • 访问开发者网站,网址:developer.paypal.com/ ,并使用注册好的商家账号进行登录
  • 创建应用,并选择类型为sandbox,并记录ClientId和Secret

image.png

1630233090(1).png

  • 配置商家和个人账户 点击左侧[SandBox下的Accounts]创建两个账户,一个商家账户,一个个人账户,商家账户会默认关联我们上一步创建的应用。

1630233263(1).png

添加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…