java对接paypal支付应用实例 (v2)
依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--paypal-->
<dependency>
<groupId>com.paypal.sdk</groupId>
<artifactId>rest-api-sdk</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>com.paypal.sdk</groupId>
<artifactId>checkout-sdk</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>com.alipay</groupId>
<artifactId>alipay_sdk</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Swagger 接口文档 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Controller
package com.example.demo.controller;
import com.example.demo.common.AcActivityLogCode;
import com.example.demo.common.ResponseData;
import com.example.demo.common.RestResult;
import com.example.demo.service.PaymentV2Service;
import com.example.demo.until.PayPalClient;
import com.example.demo.until.URLUtils;
import com.paypal.base.rest.PayPalRESTException;
import com.paypal.http.HttpResponse;
import com.paypal.orders.*;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Objects;
/**
* @program:
* @description:
* @author:
* @create:
**/
@RestController
@Validated
public class PaymentV2Controller {
/**
* 成功页面
*/
public static final String PAYPAL_SUCCESS_URL = "paypalSuccess.html";
/**
* 取消页面
*/
public static final String PAYPAL_CANCEL_URL = "pay/cancel";
@Value("${paypal.web.url}")
public String payWebUrl ;
@Value("${paypal.client.app}")
public String clientId ;
@Value("${paypal.client.secret}")
public String secret ;
@Value("${paypal.mode}")
public String mode ;
@Resource
private PaymentV2Service paymentV2Service;
@ApiOperation(value = "paypal充值表单", notes = "paypal充值表单")
@PostMapping("/pay")
@ApiImplicitParams({
@ApiImplicitParam(name = "amount", value = "充值金额", required = true, dataType = "String"),
@ApiImplicitParam(name = "currency", value = "充值金额类型", required = true, dataType = "String")
})
public ResponseData payment(
@RequestParam(value = "amount") String amount,
@RequestParam(value = "currency",defaultValue = "USD") String currency,
@RequestParam String productType,
@RequestParam(value = "couponUsageId",required = false) String couponUsageId,
HttpServletRequest request){
//设置取消页面
String cancelUrl = URLUtils.getBaseURl(request) + "/" + PAYPAL_CANCEL_URL;
//设置成功页面
String successUrl = payWebUrl + PAYPAL_SUCCESS_URL;
String sHtmlText = "";
try {
//订单表单
sHtmlText = paymentV2Service.createPayment(amount, currency, cancelUrl, successUrl);
//可根据需要 新增一条代付款记录(回调执行支付的时候 规避刷新页面重复扣费)
} catch (PayPalRESTException e) {
logger.error("创建订单表单异常:{}"+e.toString());
}
return ResponseData.ok(sHtmlText);
}
/**
* 执行支付
*/
@GetMapping(value = "pay/success")
public ResponseData successPay(
@RequestParam("token") String token) {
//可以根据新增的付款记录规避重复扣费 参数新增orderId
RestResult restResult = new RestResult();
//捕获订单 进行支付
HttpResponse<Order> response = null;
OrdersCaptureRequest ordersCaptureRequest = new OrdersCaptureRequest(token);
ordersCaptureRequest.requestBody(new OrderRequest());
PayPalClient payPalClient = new PayPalClient();
try {
//环境判定sanbox 或 live
response = payPalClient.client(mode, clientId, secret).execute(ordersCaptureRequest);
} catch (IOException e) {
logger.error("调用paypal扣款失败,失败原因 {}", e.getMessage());
}
for (PurchaseUnit purchaseUnit : response.result().purchaseUnits()) {
for (Capture capture : purchaseUnit.payments().captures()) {
if ("COMPLETED".equals(capture.status())) {
//支付成功
restResult.setMessage(Objects.requireNonNull(AcActivityLogCode.fromCode(restResult.getResultCode())).getMessage());
return ResponseData.ok(restResult);
}else{
//支付失败
restResult.setResultCode(AcActivityLogCode.PAYPAL_RECHARGE_FAIL.getCode());
restResult.setMessage(Objects.requireNonNull(AcActivityLogCode.fromCode(restResult.getResultCode())).getMessage());
}
}
}
restResult.setMessage(Objects.requireNonNull(AcActivityLogCode.fromCode(restResult.getResultCode())).getMessage());
return ResponseData.ok(restResult);
}
}
Service
package com.example.demo.service;
import com.paypal.base.rest.PayPalRESTException;
/**
* @program:
* @description:
* @author:
* @create:
**/
public interface PaymentV2Service {
/**
* 创建支付
* @param totalMoney
* @param currency
* @param cancelUrl
* @param successUrl
* @return
*/
String createPayment(String totalMoney, String currency, String cancelUrl, String successUrl) throws PayPalRESTException;
}
Impl
package com.example.demo.impl;
import com.example.demo.config.PayPalSubmit;
import com.example.demo.service.PaymentV2Service;
import com.example.demo.until.PayPalClient;
import com.paypal.base.rest.PayPalRESTException;
import com.paypal.core.PayPalHttpClient;
import com.paypal.http.HttpResponse;
import com.paypal.orders.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.*;
/**
* @program:
* @description:
* @author:
* @create:
**/
@Service
public class PaymentV2ServiceImpl implements PaymentV2Service {
@Value("${paypal.client.app}")
public String clientId ;
@Value("${paypal.client.secret}")
public String secret ;
@Value("${paypal.mode}")
public String mode ;
@Override
public String createPayment(String totalMoney, String currency, String cancelUrl, String successUrl) throws PayPalRESTException {
PayPalClient payPalClient = new PayPalClient();
// 设置环境沙盒或生产
PayPalHttpClient client = payPalClient.client(mode, clientId, secret);
//回调参数
Map<String, String> sParaTemp = new HashMap<String, String>();
//回调的参数 可以多设置几个
sParaTemp.put("totalMoney", totalMoney);
String url = successUrl + paramsConvertUrl(sParaTemp);
// 配置请求参数
OrderRequest orderRequest = new OrderRequest();
orderRequest.checkoutPaymentIntent("CAPTURE");
List<PurchaseUnitRequest> purchaseUnits = new ArrayList<>();
purchaseUnits.add(new PurchaseUnitRequest().amountWithBreakdown(new AmountWithBreakdown().currencyCode(currency).value(totalMoney)));
orderRequest.purchaseUnits(purchaseUnits);
orderRequest.applicationContext(new ApplicationContext().returnUrl(url).cancelUrl(cancelUrl));
OrdersCreateRequest request = new OrdersCreateRequest().requestBody(orderRequest);
HttpResponse<Order> response;
try {
response = client.execute(request);
Order order = response.result();
String token = order.id();
String payHref = null;
String status = order.status();
if (status.equals("CREATED")) {
List<LinkDescription> links = order.links();
for (LinkDescription linkDescription : links) {
if (linkDescription.rel().equals("approve")) {
payHref = linkDescription.href();
}
}
}
Map<String, String> resultMap = new HashMap<String, String>();
resultMap.put("token", token);
return PayPalSubmit.buildRequest(resultMap,payHref, "get", "确认");
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private static String paramsConvertUrl(Map<String, String> params) {
StringBuilder urlParams = new StringBuilder("?");
Set<Map.Entry<String, String>> entries = params.entrySet();
for (Map.Entry<String, String> entry : params.entrySet()) {
urlParams.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
}
String urlParamsStr = urlParams.toString();
return urlParamsStr.substring(0, urlParamsStr.length()-1);
}
}
until
package com.example.demo.until;
import com.paypal.core.PayPalEnvironment;
import com.paypal.core.PayPalHttpClient;
/**
* @program:
* @description:
* @author:
* @create:
**/
public class PayPalClient {
public PayPalHttpClient client(String mode, String clientId, String clientSecret) {
PayPalEnvironment environment = mode.equals("live") ? new PayPalEnvironment.Live(clientId, clientSecret) : new PayPalEnvironment.Sandbox(clientId, clientSecret);
return new PayPalHttpClient(environment);
}
}
common
package com.example.demo.config;
import com.alipay.util.AlipayCore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @program:
* @description:
* @author:
* @create:
**/
public class PayPalSubmit {
private static final Logger logger = LoggerFactory.getLogger(PayPalSubmit.class);
public static String buildRequest(Map<String,String> sParaTemp, String linkUrl, String strMethod, String strButtonName) {
Map<String, String> sPara = buildRequestPara(sParaTemp);
List<String> keys = new ArrayList(sPara.keySet());
StringBuffer sbHtml = new StringBuffer();
sbHtml.append("<form id=\"paypalsubmit\" name=\"paypalsubmit\" action=\""+ linkUrl + "\" method=\"" + strMethod + "\">");
for(int i = 0; i < keys.size(); ++i) {
String name = (String)keys.get(i);
String value = (String)sPara.get(name);
sbHtml.append("<input type=\"hidden\" name=\"" + name + "\" value=\"" + value + "\"/>");
}
sbHtml.append("<input type=\"submit\" value=\"" + strButtonName + "\" style=\"display:none;\"></form>");
sbHtml.append("<script>document.forms['paypalsubmit'].submit();</script>");
logger.debug("表单信息:"+sbHtml.toString());
return sbHtml.toString();
}
private static Map<String,String> buildRequestPara(Map<String,String> sParaTemp) {
Map<String, String> sPara = AlipayCore.paraFilter(sParaTemp);
return sPara;
}
}
application配置文件
#测试服务地址
paypal.web.url="xxxx"
#sanbox沙盒配置
paypal.mode=sandbox
paypal.client.app=AYxj25IuPg4rAQbUmOZCSIEdmji-HKDgZUHOAGVPWo74dqVUnW1q0_WUo********nYSNJd5W9KT
paypal.client.secret=EKcdUZKpu5XPEQiFFh85kFAexIDlc0a-AlVHeWCx6iLyhh4xm42tq********d8XUxYY-0WD8i
#线上服务地址
paypal.web.url="xxxx"
#live上线配置
paypal.mode=live
paypal.client.app=AYxj25IuPg4rAQbUmOZCSIEdmji-HKDgZUHOAGVPWo74dqVUnW1q0_WUo********nYSNJd5W9KT
paypal.client.secret=EKcdUZKpu5XPEQiFFh85kFAexIDlc0a-AlVHeWCx6iLyhh4xm42tq********d8XUxYY-0WD8i
基础知识不是很清楚的 请参考本博客V1版本链接: https://blog.csdn.net/m0_37693638/article/details/110930429.
实际开发过程中 需要用到定时, 参考博客中定时开发链接: https://blog.csdn.net/m0_37693638/article/details/110556412.(目的避免支付出现网络波动支付成功未到账问题)