2
0

feat: paypal支付

This commit is contained in:
caiyuchao
2025-04-23 15:50:01 +08:00
parent 517c1e7d96
commit 45d78ed7d3
6 changed files with 179 additions and 320 deletions

View File

@@ -104,9 +104,19 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.javen205</groupId> <groupId>com.paypal.sdk</groupId>
<artifactId>IJPay-PayPal</artifactId> <artifactId>paypal-server-sdk</artifactId>
<version>${ijapy.version}</version> <version>1.0.0</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>

View File

@@ -1,5 +1,6 @@
package org.wfc.payment.domain; package org.wfc.payment.domain;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -9,53 +10,11 @@ import org.springframework.stereotype.Component;
* *
* @author cyc * @author cyc
*/ */
@Data
@Component @Component
@ConfigurationProperties(prefix = "paypal") @ConfigurationProperties(prefix = "paypal")
public class PayPalBean { public class PayPalBean {
private String clientId; private String clientId;
private String secret; private String clientSecret;
private Boolean sandBox; private Boolean sandBox;
private String domain;
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public Boolean getSandBox() {
return sandBox;
}
public void setSandBox(Boolean sandBox) {
this.sandBox = sandBox;
}
public String getDomain() {
return domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
@Override
public String toString() {
return "PayPalBean{" +
"clientId='" + clientId + '\'' +
", secret='" + secret + '\'' +
", sandBox=" + sandBox +
", domain='" + domain + '\'' +
'}';
}
} }

View File

@@ -1,297 +1,82 @@
/**
*
* <p>PayPal 支付示例</p>
*
* @author cyc
*/
package org.wfc.payment.pay.paypal.controller; package org.wfc.payment.pay.paypal.controller;
import cn.hutool.json.JSONArray; import com.paypal.sdk.Environment;
import cn.hutool.json.JSONObject; import com.paypal.sdk.PaypalServerSdkClient;
import cn.hutool.json.JSONUtil; import com.paypal.sdk.authentication.ClientCredentialsAuthModel;
import com.ijpay.core.IJPayHttpResponse; import com.paypal.sdk.models.Order;
import com.ijpay.core.kit.HttpKit; import lombok.extern.slf4j.Slf4j;
import com.ijpay.core.kit.PayKit; import org.springframework.context.annotation.Bean;
import com.ijpay.paypal.PayPalApi; import org.springframework.web.bind.annotation.PathVariable;
import com.ijpay.paypal.PayPalApiConfig; import org.springframework.web.bind.annotation.PostMapping;
import com.ijpay.paypal.PayPalApiConfigKit;
import com.ijpay.paypal.accesstoken.AccessToken;
import com.ijpay.paypal.accesstoken.AccessTokenKit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.wfc.common.core.domain.R;
import org.wfc.payment.domain.PayPalBean; import org.wfc.payment.domain.PayPalBean;
import org.wfc.payment.pay.paypal.service.IPayPalService;
import javax.servlet.http.HttpServletRequest; import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@Controller @Slf4j
@RequestMapping("/payPal") @RestController
public class PayPalController { public class PayPalController {
private Logger log = LoggerFactory.getLogger(this.getClass());
@Autowired @Resource
PayPalBean payPalBean; private PayPalBean payPalBean;
private final static String RETURN_URL = "/payPal/return"; @Bean
private final static String CANCEL_URL = "/payPal/cancel"; public RestTemplate restTemplate() {
return new RestTemplate();
@RequestMapping("test")
@ResponseBody
public PayPalBean test() {
return payPalBean;
} }
public PayPalApiConfig getConfig() { @Bean
PayPalApiConfig config = new PayPalApiConfig(); public PaypalServerSdkClient paypalClient() {
config.setClientId(payPalBean.getClientId()); return new PaypalServerSdkClient.Builder()
config.setSecret(payPalBean.getSecret()); // .loggingConfig(builder -> builder
config.setSandBox(payPalBean.getSandBox()); // .level(Level.DEBUG)
config.setDomain(payPalBean.getDomain()); // .requestConfig(logConfigBuilder -> logConfigBuilder.body(true))
PayPalApiConfigKit.setThreadLocalApiConfig(config); // .responseConfig(logConfigBuilder -> logConfigBuilder.headers(true)))
return config; .httpClientConfig(configBuilder -> configBuilder
} .timeout(0))
.environment(payPalBean.getSandBox() ? Environment.SANDBOX : Environment.PRODUCTION)
.clientCredentialsAuth(new ClientCredentialsAuthModel.Builder(
payPalBean.getClientId(),
payPalBean.getClientSecret())
.build())
.build();
}
@RequestMapping(value = "/getAccessToken")
@ResponseBody
public AccessToken getAccessToken() {
try {
getConfig();
return AccessTokenKit.get();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@RequestMapping(value = "/createOrder") @RestController
@ResponseBody @RequestMapping("/paypal")
public void createOrder(HttpServletResponse response) { public class CheckoutController {
try {
PayPalApiConfig config = getConfig();
//参数请求参数文档 https://developer.paypal.com/docs/api/orders/v2/#orders_create @Resource
Map<String, Object> dataMap = new HashMap<>(); private IPayPalService payPalService;
dataMap.put("intent", "CAPTURE");
ArrayList<Map<String, Object>> list = new ArrayList<>(); @PostMapping("/orders")
public R<Order> createOrder(@RequestParam Long orderId) {
Map<String, Object> amount = new HashMap<>(); try {
amount.put("currency_code", "USD"); // String cart = objectMapper.writeValueAsString(request.get("cart"));
amount.put("value", "0.01"); Order response = payPalService.createOrder(orderId);
return R.ok(response);
Map<String, Object> itemMap = new HashMap<>(); } catch (Exception e) {
itemMap.put("amount", amount); log.error("Error creating paypal order", e);
return R.fail(e.getMessage());
list.add(itemMap);
dataMap.put("purchase_units", list);
Map<String, String> card = new HashMap<>();
card.put("name", "test buyer");
card.put("number", "4231220385792723");
card.put("security_code", "123");
card.put("expiry", "2029-07");
Map<String, String> experienceContext = new HashMap<>();
experienceContext.put("cancel_url", config.getDomain().concat(CANCEL_URL));
experienceContext.put("return_url", config.getDomain().concat(RETURN_URL));
Map<String, Map<String,String>> paymentSource = new HashMap<>();
paymentSource.put("experience_context",experienceContext);
paymentSource.put("card",card);
// dataMap.put("payment_source", paymentSource);
String data = JSONUtil.toJsonStr(dataMap);
log.info(data);
IJPayHttpResponse resData = PayPalApi.createOrder(config, data);
log.info(resData.toString());
if (resData.getStatus() == 201) {
String resultStr = resData.getBody();
JSONObject jsonObject = JSONUtil.parseObj(resultStr);
JSONArray links = jsonObject.getJSONArray("links");
for (int i = 0; i < links.size(); i++) {
JSONObject item = links.getJSONObject(i);
String rel = item.getStr("rel");
String href = item.getStr("href");
if ("approve".equalsIgnoreCase(rel)) {
response.sendRedirect(href);
break;
}
}
} }
} catch (Exception e) {
e.printStackTrace();
} }
}
@RequestMapping(value = "/updateOrder") @PostMapping("/orders/{paypalOrderId}/capture/{orderId}")
@ResponseBody public R<Order> captureOrder(@PathVariable String paypalOrderId, @PathVariable Long orderId) {
public String updateOrder(@RequestParam("id") String id) { try {
try { Order response = payPalService.captureOrders(paypalOrderId, orderId);
PayPalApiConfig config = getConfig(); return R.ok(response);
// https://developer.paypal.com/docs/api/orders/v2/#orders_patch } catch (Exception e) {
log.error("Error creating paypal capture", e);
ArrayList<Map<String, Object>> updateData = new ArrayList<>(); return R.fail(e.getMessage());
Map<String, Object> itemMap = new HashMap<>();
itemMap.put("op", "replace");
itemMap.put("path", "/purchase_units/@reference_id=='default'/amount");
Map<String, Object> amount = new HashMap<>();
amount.put("currency_code", "USD");
amount.put("value", "199.00");
itemMap.put("value", amount);
updateData.add(itemMap);
String data = JSONUtil.toJsonStr(updateData);
log.info(data);
IJPayHttpResponse resData = PayPalApi.updateOrder(config, id, data);
log.info(resData.toString());
if (resData.getStatus() == 204) {
return "success";
} }
return "接口请求错误码为:" + resData.getStatus();
} catch (Exception e) {
e.printStackTrace();
} }
return null;
}
@RequestMapping(value = "/queryOrder")
@ResponseBody
public String queryOrder(@RequestParam("id") String id) {
try {
PayPalApiConfig config = getConfig();
IJPayHttpResponse response = PayPalApi.queryOrder(config, id);
log.info(response.toString());
if (response.getStatus() == 200) {
return response.getBody();
} else {
return "接口请求错误码为:" + response.getStatus();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@RequestMapping(value = "/captureOrder")
@ResponseBody
public String captureOrder(@RequestParam("id") String id) {
try {
PayPalApiConfig config = getConfig();
IJPayHttpResponse response = PayPalApi.captureOrder(config, id, "");
log.info(response.toString());
if (response.getStatus() == 200 || response.getStatus() == 201) {
return response.getBody();
} else {
return "接口请求错误码为:" + response.getStatus();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@RequestMapping(value = "/captureQuery")
@ResponseBody
public String captureQuery(@RequestParam("captureId") String captureId) {
try {
PayPalApiConfig config = getConfig();
IJPayHttpResponse response = PayPalApi.captureQuery(config, captureId);
log.info(response.toString());
if (response.getStatus() == 200) {
return response.getBody();
} else {
return "接口请求错误码为:" + response.getStatus();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@RequestMapping(value = "/refund")
@ResponseBody
public String refund(@RequestParam("id") String id) {
try {
PayPalApiConfig config = getConfig();
System.out.println("id>" + id);
Map<String, Object> map = new HashMap<>();
map.put("invoice_id", PayKit.generateStr());
map.put("note_to_payer", "test product");
Map<String, String> amount = new HashMap<>();
amount.put("value", "1.00");
amount.put("currency_code", "USD");
map.put("amount", amount);
String data = JSONUtil.toJsonStr(map);
log.info("refund data" + data);
IJPayHttpResponse response = PayPalApi.refund(config, id, data);
log.info(response.toString());
if (response.getStatus() == 201) {
return response.getBody();
} else {
return "接口请求错误码为:" + response.getStatus();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@RequestMapping(value = "/refundQuery")
@ResponseBody
public String refundQuery(@RequestParam("id") String id) {
try {
PayPalApiConfig config = getConfig();
IJPayHttpResponse response = PayPalApi.refundQuery(config, id);
log.info(response.toString());
if (response.getStatus() == 200) {
return response.getBody();
} else {
return "接口请求错误码为:" + response.getStatus();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@RequestMapping(value = "/return")
@ResponseBody
public String returnUrl(HttpServletRequest request) {
try {
String token = request.getParameter("token");
String payerId = request.getParameter("PayerID");
log.info("token:" + token);
log.info("payerId:" + payerId);
return token;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@RequestMapping(value = "/cancel")
@ResponseBody
public String cancelUrl(HttpServletRequest request, HttpServletResponse response) {
String readData = HttpKit.readData(request);
System.out.println(readData);
return readData;
} }
} }

View File

@@ -0,0 +1,17 @@
package org.wfc.payment.pay.paypal.service;
import com.paypal.sdk.exceptions.ApiException;
import com.paypal.sdk.models.Order;
import java.io.IOException;
/**
* @author: cyc
* @since: 2025-04-23
*/
public interface IPayPalService {
Order createOrder(Long orderId) throws IOException, ApiException;
Order captureOrders(String paypalOrderId, Long orderId) throws IOException, ApiException;
}

View File

@@ -0,0 +1,83 @@
package org.wfc.payment.pay.paypal.service.impl;
import com.paypal.sdk.PaypalServerSdkClient;
import com.paypal.sdk.controllers.OrdersController;
import com.paypal.sdk.exceptions.ApiException;
import com.paypal.sdk.http.response.ApiResponse;
import com.paypal.sdk.models.AmountWithBreakdown;
import com.paypal.sdk.models.CaptureOrderInput;
import com.paypal.sdk.models.CheckoutPaymentIntent;
import com.paypal.sdk.models.CreateOrderInput;
import com.paypal.sdk.models.Order;
import com.paypal.sdk.models.OrderRequest;
import com.paypal.sdk.models.PurchaseUnitRequest;
import org.springframework.stereotype.Service;
import org.wfc.common.core.domain.R;
import org.wfc.payment.pay.paypal.service.IPayPalService;
import org.wfc.user.api.RemoteUUserService;
import org.wfc.user.api.domain.vo.UOrderVo;
import javax.annotation.Resource;
import java.io.IOException;
import java.math.RoundingMode;
import java.util.Arrays;
/**
* @author: cyc
* @since: 2025-04-23
*/
@Service
public class PaypalServiceImpl implements IPayPalService {
@Resource
private PaypalServerSdkClient client;
@Resource
private RemoteUUserService remoteUUserService;
@Override
public Order createOrder(Long orderId) throws IOException, ApiException {
String amount = getAmountByOrder(orderId);
CreateOrderInput createOrderInput = new CreateOrderInput.Builder(
null,
new OrderRequest.Builder(
CheckoutPaymentIntent.CAPTURE,
Arrays.asList(
new PurchaseUnitRequest.Builder(
new AmountWithBreakdown.Builder(
"USD",
amount)
.build())
.build()))
.build())
.build();
OrdersController ordersController = client.getOrdersController();
ApiResponse<Order> apiResponse = ordersController.createOrder(createOrderInput);
return apiResponse.getResult();
}
private String getAmountByOrder(Long orderId) {
R<UOrderVo> orderRes = remoteUUserService.getOrderById(orderId);
UOrderVo orderVo = orderRes.getData();
if (orderVo == null) {
return null;
}
return orderVo.getOrderAmount().setScale(2, RoundingMode.HALF_UP).toString();
}
@Override
public Order captureOrders(String paypalOrderId, Long orderId) throws IOException, ApiException {
CaptureOrderInput ordersCaptureInput = new CaptureOrderInput.Builder(
paypalOrderId,
null)
.build();
OrdersController ordersController = client.getOrdersController();
ApiResponse<Order> apiResponse = ordersController.captureOrder(ordersCaptureInput);
remoteUUserService.paySuccess(orderId, "inner");
return apiResponse.getResult();
}
}

View File

@@ -72,3 +72,8 @@ wxpay:
partnerKey: partnerKey:
certPath: certPath:
domain: http://192.168.9.50/u domain: http://192.168.9.50/u
paypal:
client-id: AfPgwFAmo9K7KCqiiGpNRCyQMSxI6V33eH-nEMnVndJNVEYOEOEn5wSPkHUybfzcjDLnBejt-RKnIfqX
client_secret: EOYQzSGuMaTMWodcppZUTz9v3H9j38yYiv8bmj4kLZl5NiSUJ0AJJuGlA1CU4oDtEX6jNdGNhsMCiAcN
sandBox: true