java对接PayPal支付
我们公司最近开通了网上支付功能,国内选择对接支付宝和微信,国外选择对接paypal,
今天我先将paypal对接方式记录下来,后面会记录微信和支付宝(本人比较懒,微信和支付宝的可能会需要很久才更新)
PayPal是类似于支付宝和微信这类的第三方支付工具,
区别是微信和支付宝必须有登录账号才可以支付,而PayPal可以不用登录直接选择银行卡支付
PayPal也是国外用户数最多的一个支付平台(因为一旦出现争议,PayPal偏向个人用户比较多)
PayPal有v1、v2两个版本的SDK,
网上几乎都是针对v1 的文档,v2的几乎看不到。
v1:rest-api-sdk
<dependency>
<groupId>com.paypal.sdk</groupId>
<artifactId>rest-api-sdk</artifactId>
<version>LATEST</version>
</dependency>
v2:checkout-sdk
<dependency>
<groupId>com.paypal.sdk</groupId>
<artifactId>checkout-sdk</artifactId>
<version>1.0.2</version>
</dependency>
这两个版本的我都接入过,但由于官方不建议再使用v1,所以今天我只讲v2
言归正传,正是开始
第一:maven依赖
<dependency>
<groupId>com.paypal.sdk</groupId>
<artifactId>checkout-sdk</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180813</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
第二:编写代码
1. PayPalClient(请求PayPal api的工具类)
package com.ratta.paypal.info;
import com.paypal.core.PayPalEnvironment;
import com.paypal.core.PayPalHttpClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.Iterator;
@Slf4j
public class PayPalClient {
public PayPalHttpClient client(String mode, String clientId, String clientSecret) {
log.info("mode={}, clientId={}, clientSecret={}", mode, clientId, clientSecret);
PayPalEnvironment environment = mode.equals("live") ? new PayPalEnvironment.Live(clientId, clientSecret) : new PayPalEnvironment.Sandbox(clientId, clientSecret);
return new PayPalHttpClient(environment);
}
/**
* @param jo
* @param pre
* @return
*/
public String prettyPrint(JSONObject jo, String pre) {
Iterator<?> keys = jo.keys();
StringBuilder pretty = new StringBuilder();
while (keys.hasNext()) {
String key = (String) keys.next();
pretty.append(String.format("%s%s: ", pre, StringUtils.capitalize(key)));
if (jo.get(key) instanceof JSONObject) {
pretty.append(prettyPrint(jo.getJSONObject(key), pre + "\t"));
} else if (jo.get(key) instanceof JSONArray) {
int sno = 1;
for (Object jsonObject : jo.getJSONArray(key)) {
pretty.append(String.format("\n%s\t%d:\n", pre, sno++));
pretty.append(prettyPrint((JSONObject) jsonObject, pre + "\t\t"));
}
} else {
pretty.append(String.format("%s\n", jo.getString(key)));
}
}
return pretty.toString();
}
}
2. CreateOrder (创建订单)
package com.ratta.paypal.info;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONObject;
import com.paypal.http.HttpResponse;
import com.paypal.http.serializer.Json;
import com.paypal.orders.AddressPortable;
import com.paypal.orders.AmountBreakdown;
import com.paypal.orders.AmountWithBreakdown;
import com.paypal.orders.ApplicationContext;
import com.paypal.orders.Item;
import com.paypal.orders.LinkDescription;
import com.paypal.orders.Money;
import com.paypal.orders.Name;
import com.paypal.orders.Order;
import com.paypal.orders.OrderRequest;
import com.paypal.orders.OrdersCreateRequest;
import com.paypal.orders.PurchaseUnitRequest;
import com.paypal.orders.ShippingDetail;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class CreateOrder {
private String clientId = "AdTJ2en6Av42r1xoziJj6bJK-X4tRGDHACZId0OPXfGyXs8OyoYEmlm8bHjzrgd3UislDQR0iBP7x-wM";
private String clientSecret = "EC9DSrfAfVfo-c3K-1dILXA5iijnHtaunKwv2JzECSz9jcdy3t78rDFeAEgaixnnIYYlgQcipbZQWoCa";
private String mode = "sandbox";
public static final String CAPTURE = "CAPTURE";
/**
* 该标签将覆盖PayPal网站上PayPal帐户中的公司名称
*/
public static final String BRANDNAME = "Supernote";
/**
* LOGIN。当客户单击PayPal Checkout时,客户将被重定向到页面以登录PayPal并批准付款。
* BILLING。当客户单击PayPal Checkout时,客户将被重定向到一个页面,以输入信用卡或借记卡以及完成购买所需的其他相关账单信息
* NO_PREFERENCE。当客户单击“ PayPal Checkout”时,将根据其先前的交互方式将其重定向到页面以登录PayPal并批准付款,或重定向至页面以输入信用卡或借记卡以及完成购买所需的其他相关账单信息使用PayPal。
* 默认值:NO_PREFERENCE
*/
public static final String LANDINGPAGE = "NO_PREFERENCE";
/**
* CONTINUE。将客户重定向到PayPal付款页面后,将出现“ 继续”按钮。当结帐流程启动时最终金额未知时,请使用此选项,并且您想将客户重定向到商家页面而不处理付款。
* PAY_NOW。将客户重定向到PayPal付款页面后,出现“ 立即付款”按钮。当启动结帐时知道最终金额并且您要在客户单击“ 立即付款”时立即处理付款时,请使用此选项。
*/
public static final String USERACTION = "CONTINUE";
/**
* GET_FROM_FILE。使用贝宝网站上客户提供的送货地址。
* NO_SHIPPING。从PayPal网站编辑送货地址。推荐用于数字商品
* SET_PROVIDED_ADDRESS。使用商家提供的地址。客户无法在PayPal网站上更改此地址
*/
public static final String SHIPPINGPREFERENCE = "SET_PROVIDED_ADDRESS";
/**
* 生成订单主体信息
*/
private OrderRequest buildRequestBody() {
OrderRequest orderRequest = new OrderRequest();
orderRequest.checkoutPaymentIntent(CAPTURE);
ApplicationContext applicationContext = new ApplicationContext()
.brandName(BRANDNAME)
.landingPage(LANDINGPAGE)
.cancelUrl("https://www.example.com").returnUrl("https://www.example.com")
.userAction(USERACTION)
.shippingPreference(SHIPPINGPREFERENCE);
orderRequest.applicationContext(applicationContext);
List<PurchaseUnitRequest> purchaseUnitRequests = new ArrayList<PurchaseUnitRequest>();
@SuppressWarnings("serial")
PurchaseUnitRequest purchaseUnitRequest = new PurchaseUnitRequest()
.description("新一代读写一体,智能电子笔记本")
.customId("P2020052514440001")
.invoiceId("P2020052514440001")
.amountWithBreakdown(new AmountWithBreakdown()
.currencyCode("USD")
.value("220.00")
.amountBreakdown(new AmountBreakdown()
.itemTotal(new Money().currencyCode("USD").value("220.00"))
.shipping(new Money().currencyCode("USD").value("0.00"))
.handling(new Money().currencyCode("USD").value("0.00"))
.taxTotal(new Money().currencyCode("USD").value("0.00"))
.shippingDiscount(new Money().currencyCode("USD").value("0.00"))))
.items(new ArrayList<Item>() {
{
add(new Item().name("Supernote A6").description("丝滑般流畅的书写体验")
.unitAmount(new Money()
.currencyCode("USD")
.value("200.00"))
.quantity("1"));
add(new Item().name("帆布封套").description("黑色帆布保护封套")
.unitAmount(new Money()
.currencyCode("USD")
.value("20.00"))
.quantity("1"));
}
})
.shippingDetail(new ShippingDetail()
.name(new Name().fullName("RATTA"))
.addressPortable(new AddressPortable()
.addressLine1("梅陇镇")
.addressLine2("集心路168号")
.adminArea2("闵行区")
.adminArea1("上海市")
.postalCode("20000")
.countryCode("CN")));
purchaseUnitRequests.add(purchaseUnitRequest);
orderRequest.purchaseUnits(purchaseUnitRequests);
return orderRequest;
}
/**
* 创建订单的方法
* @throws 收银台地址
*/
public String createOrder() throws IOException {
OrdersCreateRequest request = new OrdersCreateRequest();
request.header("prefer","return=representation");
request.requestBody(buildRequestBody());
PayPalClient payPalClient = new PayPalClient();
HttpResponse<Order> response = null;
try {
response = payPalClient.client(mode, clientId, clientSecret).execute(request);
} catch (IOException e1) {
try {
log.error("第1次调用paypal订单创建失败");
response = payPalClient.client(mode, clientId, clientSecret).execute(request);
} catch (Exception e) {
try {
log.error("第2次调用paypal订单创建失败");
response = payPalClient.client(mode, clientId, clientSecret).execute(request);
} catch (Exception e2) {
log.error("第3次调用paypal订单创建失败,失败原因:{}", e2.getMessage());
}
}
}
String approve = "";
if (response.statusCode() == 201) {
log.info("Status Code = {}, Status = {}, OrderID = {}, Intent = {}", response.statusCode(), response.result().status(), response.result().id(), response.result().checkoutPaymentIntent());
for (LinkDescription link : response.result().links()) {
log.info("Links-{}: {} \tCall Type: {}", link.rel(), link.href(), link.method());
if(link.rel().equals("approve")) {
approve = link.href();
}
}
String totalAmount = response.result().purchaseUnits().get(0).amountWithBreakdown().currencyCode() + ":" + response.result().purchaseUnits().get(0).amountWithBreakdown().value();
log.info("Total Amount: {}", totalAmount);
String json= new JSONObject(new Json().serialize(response.result())).toString(4);
log.info("createOrder response body: {}", json);
}
return approve;
}
public static void main(String args[]) {
try {
String approveUrl = new CreateOrder().createOrder();
System.out.println("approveUrl = "+ approveUrl);
} catch (com.paypal.http.exceptions.HttpException e) {
System.out.println(e.getLocalizedMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
}
3. CaptureOrder(执行扣款)
用户通过CreateOrder生成 approveUrl 跳转paypal支付成功后,只是授权,并没有将用户的钱打入我们的paypal账户,我们需要通过 CaptureOrder接口,将钱打入我的PayPal账户
package com.ratta.paypal.info;
import java.io.IOException;
import com.paypal.orders.*;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONObject;
import com.paypal.http.HttpResponse;
import com.paypal.http.serializer.Json;
@Slf4j
public class CaptureOrder extends PayPalClient {
private String clientId = "AdTJ2en6Av42r1xoziJj6bJK-X4tRGDHACZId0OPXfGyXs8OyoYEmlm8bHjzrgd3UislDQR0iBP7x-wM";
private String clientSecret = "EC9DSrfAfVfo-c3K-1dILXA5iijnHtaunKwv2JzECSz9jcdy3t78rDFeAEgaixnnIYYlgQcipbZQWoCa";
private String mode = "sandbox";
public OrderRequest buildRequestBody() {
return new OrderRequest();
}
/**
* 用户授权支付成功,进行扣款操作
*/
public HttpResponse<Order> captureOrder(String orderId) throws IOException {
OrdersCaptureRequest request = new OrdersCaptureRequest(orderId);
request.requestBody(new OrderRequest());
PayPalClient payPalClient = new PayPalClient();
HttpResponse<Order> response = null;
try {
response = payPalClient.client(mode, clientId, clientSecret).execute(request);
} catch (IOException e1) {
try {
log.error("第1次调用paypal扣款失败");
response = payPalClient.client(mode, clientId, clientSecret).execute(request);
} catch (Exception e) {
try {
log.error("第2次调用paypal扣款失败");
response = payPalClient.client(mode, clientId, clientSecret).execute(request);
} catch (Exception e2) {
log.error("第3次调用paypal扣款失败,失败原因 {}", e2.getMessage() );
}
}
}
log.info("Status Code = {}, Status = {}, OrderID = {}", response.statusCode(), response.result().status(), response.result().id());
for (LinkDescription link : response.result().links()) {
log.info("Links-{}: {} \tCall Type: {}", link.rel(), link.href(), link.method());
}
for (PurchaseUnit purchaseUnit : response.result().purchaseUnits()) {
for (Capture capture : purchaseUnit.payments().captures()) {
log.info("Capture id: {}", capture.id());
log.info("status: {}", capture.status());
log.info("status_details: {}", capture.captureStatusDetails().reason());
log.info("invoice_id: {}", capture.invoiceId());
if("COMPLETED".equals(capture.status())) {
//进行数据库操作,修改订单状态为已支付成功,尽快发货(配合回调和CapturesGet查询确定成功)
log.info("支付成功,状态为=COMPLETED");
}
if("PENDING".equals(capture.status())) {
String reason = "PENDING";
if(capture.captureStatusDetails() != null && capture.captureStatusDetails().reason() != null) {
reason = capture.captureStatusDetails().reason();
}
//进行数据库操作,修改订单状态为已支付成功,但触发了人工审核,请审核通过后再发货(配合回调和CapturesGet查询确定成功)
log.info("支付成功,状态为=PENDING : {}", reason);
}
}
}
Payer buyer = response.result().payer();
log.info("Buyer Email Address: {}", buyer.email());
log.info("Buyer Name: {} {}", buyer.name().givenName(), buyer.name().surname());
String json = new JSONObject(new Json().serialize(response.result())).toString(4);
log.info("captureOrder response body: {}", json);
return response;
}
public static void main(String[] args) {
try {
new CaptureOrder().captureOrder("订单id,CreateOrder 生成");
} catch (Exception e) {
e.printStackTrace();
}
}
}
4. RefundOrder(申请退款)
package com.ratta.paypal.info;
import java.io.IOException;
import org.json.JSONObject;
import com.paypal.http.HttpResponse;
import com.paypal.http.serializer.Json;
import com.paypal.orders.OrdersGetRequest;
import com.paypal.payments.CapturesRefundRequest;
import com.paypal.payments.Money;
import com.paypal.payments.Refund;
import com.paypal.payments.RefundRequest;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class RefundOrder extends PayPalClient {
private String clientId = "AdTJ2en6Av42r1xoziJj6bJK-X4tRGDHACZId0OPXfGyXs8OyoYEmlm8bHjzrgd3UislDQR0iBP7x-wM";
private String clientSecret = "EC9DSrfAfVfo-c3K-1dILXA5iijnHtaunKwv2JzECSz9jcdy3t78rDFeAEgaixnnIYYlgQcipbZQWoCa";
private String mode = "sandbox";
/**
* 创建退款请求体
*/
public RefundRequest buildRequestBody() {
RefundRequest refundRequest = new RefundRequest();
Money money = new Money();
money.currencyCode("USD");
money.value("40.00");
refundRequest.amount(money);
refundRequest.invoiceId("T202005230002");
refundRequest.noteToPayer("7天无理由退款");
return refundRequest;
}
/**
* 申请退款
*/
public HttpResponse<Refund> refundOrder(String orderId) throws IOException {
OrdersGetRequest ordersGetRequest = new OrdersGetRequest(orderId);
PayPalClient payPalClient = new PayPalClient();
HttpResponse<com.paypal.orders.Order> ordersGetResponse = null;
try {
ordersGetResponse = payPalClient.client(mode, clientId, clientSecret).execute(ordersGetRequest);
} catch (Exception e) {
try {
log.error("第1次调用paypal订单查询失败");
ordersGetResponse = payPalClient.client(mode, clientId, clientSecret).execute(ordersGetRequest);
} catch (Exception e2) {
try {
log.error("第2次调用paypal订单查询失败");
ordersGetResponse = payPalClient.client(mode, clientId, clientSecret).execute(ordersGetRequest);
} catch (Exception e3) {
log.error("第3次调用paypal订单查询失败,失败原因:{}", e3.getMessage());
}
}
}
String captureId = ordersGetResponse.result().purchaseUnits().get(0).payments().captures().get(0).id();
CapturesRefundRequest request = new CapturesRefundRequest(captureId);
request.prefer("return=representation");
request.requestBody( buildRequestBody());
HttpResponse<Refund> response = null;
try {
response = payPalClient.client(mode, clientId, clientSecret).execute(request);
} catch (IOException e) {
try {
log.error("第1次调用paypal退款申请失败");
response = payPalClient.client(mode, clientId, clientSecret).execute(request);
} catch (Exception e1) {
try {
log.error("第2次调用paypal退款申请失败");
response = payPalClient.client(mode, clientId, clientSecret).execute(request);
} catch (Exception e2) {
log.error("第3次调用paypal退款申请失败,失败原因 {}", e2.getMessage());
}
}
}
log.info("Status Code = {}, Status = {}, RefundID = {}", response.statusCode(), response.result().status(), response.result().id());
if("COMPLETED".equals(response.result().status())) {
//进行数据库操作,修改状态为已退款(配合回调和退款查询确定退款成功)
log.info("退款成功");
}
for (com.paypal.payments.LinkDescription link : response.result().links()) {
log.info("Links-{}: {} \tCall Type: {}", link.rel(), link.href(), link.method());
}
String json = new JSONObject(new Json().serialize(response.result())).toString(4);
log.info("refundOrder response body: {}", json);
return response;
}
public static void main(String[] args) {
try {
new RefundOrder().refundOrder("订单id,CreateOrder 生成");
} catch (Exception e) {
e.printStackTrace();
}
}
}
5. OrdersGet(查询订单详情)
package com.ratta.paypal.info;
import java.io.IOException;
import java.util.List;
import org.json.JSONObject;
import com.paypal.http.HttpResponse;
import com.paypal.http.serializer.Json;
import com.paypal.orders.Capture;
import com.paypal.orders.Order;
import com.paypal.orders.OrdersGetRequest;
import com.paypal.orders.Refund;
public class OrdersGet extends PayPalClient {
private String clientId = "AdTJ2en6Av42r1xoziJj6bJK-X4tRGDHACZId0OPXfGyXs8OyoYEmlm8bHjzrgd3UislDQR0iBP7x-wM";
private String clientSecret = "EC9DSrfAfVfo-c3K-1dILXA5iijnHtaunKwv2JzECSz9jcdy3t78rDFeAEgaixnnIYYlgQcipbZQWoCa";
private String mode = "sandbox";
public void testOrdersGetRequest() throws IOException {
OrdersGetRequest request = new OrdersGetRequest("订单id,CreateOrder 生成");
HttpResponse<Order> response = null;
try {
response = client(mode, clientId, clientSecret).execute(request);
} catch (Exception e) {
try {
System.out.println("调用paypal订单查询失败,链接异常1");
response = client(mode, clientId, clientSecret).execute(request);
} catch (Exception e2) {
try {
System.out.println("调用paypal订单查询失败,链接异常2");
response = client(mode, clientId, clientSecret).execute(request);
} catch (Exception e3) {
System.out.println("调用paypal订单查询失败,链接异常3");
System.out.println(e3.getMessage());
}
}
}
System.out.println("Status Code: " + response.statusCode());
System.out.println("Status: " + response.result().status());
System.out.println("Order id: " + response.result().id());
if(response.result().purchaseUnits().get(0).payments() != null) {
List<Capture> captures = response.result().purchaseUnits().get(0).payments().captures();
if(captures != null) {
for (Capture capture : captures) {
System.out.println("\t订单编号= " + capture.invoiceId() + "\tCapture Id= " + capture.id() + "\tCapture status= " + capture.status() + "\tCapture amount= " + capture.amount().currencyCode() + ":" + capture.amount().value());
}
}
List<Refund> refunds = response.result().purchaseUnits().get(0).payments().refunds();
if(refunds != null) {
for (Refund refund : refunds) {
System.out.println("\t售后编号= " + refund.invoiceId() + "\tRefund Id= " + refund.id() + "\tRefund status= " + refund.status() + "\tRefund amount= " + refund.amount().currencyCode() + ":" + refund.amount().value());
}
}
}
System.out.println("Links: ");
for (com.paypal.orders.LinkDescription link : response.result().links()) {
System.out.println("\t" + link.rel() + ": " + link.href() + "\tCall Type: " + link.method());
}
System.out.println("Full response body:");
String json = new JSONObject(new Json().serialize(response.result())).toString(4);
System.out.println(json);
}
public static void main(String[] args) {
try {
new OrdersGet().testOrdersGetRequest();
} catch (IOException e) {
e.printStackTrace();
}
}
}
6. CapturesGet(查询扣款详情)
package com.ratta.paypal.info;
import java.io.IOException;
import org.json.JSONObject;
import com.paypal.http.HttpResponse;
import com.paypal.http.serializer.Json;
import com.paypal.payments.Capture;
import com.paypal.payments.CapturesGetRequest;
import com.paypal.payments.LinkDescription;
public class CapturesGet extends PayPalClient {
private String clientId = "AdTJ2en6Av42r1xoziJj6bJK-X4tRGDHACZId0OPXfGyXs8OyoYEmlm8bHjzrgd3UislDQR0iBP7x-wM";
private String clientSecret = "EC9DSrfAfVfo-c3K-1dILXA5iijnHtaunKwv2JzECSz9jcdy3t78rDFeAEgaixnnIYYlgQcipbZQWoCa";
private String mode = "sandbox";
public void testCapturesGetRequest() throws IOException {
CapturesGetRequest request = new CapturesGetRequest("扣款id, CaptureOrder生成");
HttpResponse<Capture> response = client(mode, clientId, clientSecret).execute(request);
System.out.println("Status Code: " + response.statusCode());
System.out.println("Status: " + response.result().status());
System.out.println("Capture ids: " + response.result().id());
System.out.println("Links: ");
for (LinkDescription link : response.result().links()) {
System.out.println("\t" + link.rel() + ": " + link.href() + "\tCall Type: " + link.method());
}
System.out.println("Full response body:");
System.out.println(new JSONObject(new Json().serialize(response.result())).toString(4));
}
public static void main(String[] args) {
try {
new CapturesGet().testCapturesGetRequest();
} catch (IOException e) {
e.printStackTrace();
}
}
}
7. RefundsGet(查询退款详情)
package com.ratta.paypal.info;
import com.paypal.http.HttpResponse;
import com.paypal.http.serializer.Json;
import com.paypal.payments.LinkDescription;
import com.paypal.payments.Refund;
import com.paypal.payments.RefundsGetRequest;
import org.json.JSONObject;
import java.io.IOException;
public class RefundsGet extends PayPalClient {
private String clientId = "AdTJ2en6Av42r1xoziJj6bJK-X4tRGDHACZId0OPXfGyXs8OyoYEmlm8bHjzrgd3UislDQR0iBP7x-wM";
private String clientSecret = "EC9DSrfAfVfo-c3K-1dILXA5iijnHtaunKwv2JzECSz9jcdy3t78rDFeAEgaixnnIYYlgQcipbZQWoCa";
private String mode = "sandbox";
public void testRefundsGetRequest() throws IOException {
RefundsGetRequest request = new RefundsGetRequest("5LG75297SL032181K");
HttpResponse<Refund> response = client(mode, clientId, clientSecret).execute(request);
System.out.println("Status Code: " + response.statusCode());
System.out.println("Status: " + response.result().status());
System.out.println("Refund Id: " + response.result().id());
System.out.println("Links: ");
for (LinkDescription link : response.result().links()) {
System.out.println("\t" + link.rel() + ": " + link.href() + "\tCall Type: " + link.method());
}
System.out.println("Full response body:");
System.out.println(new JSONObject(new Json().serialize(response.result())).toString(4));
}
public static void main(String[] args) {
try {
new RefundsGet().testRefundsGetRequest();
} catch (IOException e) {
e.printStackTrace();
}
}
}
到了这里基本上已经接入完毕, 现在还剩余异步回调,PayPal的异步回调我使用的 是IPN ,这个回调是需要登录进你的PayPal账号,选择账户设置——>选择通知——>选择及时付款通知——>点击更新——>填写你的回调地址路径,开启并保存
8.PayPalController(PayPal控制层代码)
package com.ratta.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.ratta.service.PayPalCheckoutService;
import com.ratta.util.RequestToMapUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
@RestController
@Api(description = "PayPalCheckout接口")
public class PayPalCheckoutController {
@Autowired
private PayPalCheckoutService payPalCheckoutService;
@ApiOperation(value = "ipn异步回调")
@PostMapping(value = "/paypal/ipn/back")
public String callback(HttpServletRequest request, HttpServletResponse response) {
return payPalCheckoutService.callback(RequestToMapUtil.getParameterMap(request));
}
}
9.RequestToMapUtil
package com.ratta.util;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
/**
* 将Request转换成Map
* @author yll
*
*/
public class RequestToMapUtil {
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Map getParameterMap(HttpServletRequest request) {
// 参数Map
Map properties = request.getParameterMap();
// 返回值Map
Map returnMap = new HashMap();
Iterator entries = properties.entrySet().iterator();
Map.Entry entry;
String name = "";
String value = "";
while (entries.hasNext()) {
entry = (Map.Entry) entries.next();
name = (String) entry.getKey();
Object valueObj = entry.getValue();
if (null == valueObj) {
value = "";
} else if (valueObj instanceof String[]) {
String[] values = (String[]) valueObj;
for (int i = 0; i < values.length; i++) {
value = values[i] + ",";
}
value = value.substring(0, value.length() - 1);
} else {
value = valueObj.toString();
}
returnMap.put(name, value);
}
return returnMap;
}
public static Map<String, Object> getPrepayMapInfo(String Str) {
String notityXml = Str.replaceAll("</?xml>", "");
Pattern pattern = Pattern.compile("<.*?/.*?>");
Matcher matcher = pattern.matcher(notityXml);
Pattern pattern2 = Pattern.compile("!.*]");
Map<String, Object> mapInfo = new HashMap<>();
while (matcher.find()) {
String key = matcher.group().replaceAll(".*/", "");
key = key.substring(0, key.length() - 1);
Matcher matcher2 = pattern2.matcher(matcher.group());
String value = matcher.group().replaceAll("</?.*?>", "");
if (matcher2.find() && !value.equals("DATA")) {
value = matcher2.group().replaceAll("!.*\\[", "");
value = value.substring(0, value.length() - 2);
}
mapInfo.put(key, value);
}
return mapInfo;
}
}
10.PayPalCheckoutService
package com.ratta.service;
import java.util.Map;
import com.ratta.dto.CreateOrderDTO;
import com.ratta.dto.ExecuteOrderDTO;
import com.ratta.dto.RefundOrderDTO;
import com.ratta.vo.BaseVO;
import com.ratta.vo.RefundOrderVO;
public interface PayPalCheckoutService {
/**
* 回调
* @param map
*/
String callback(@SuppressWarnings("rawtypes") Map map);
}
11.PayPalCheckoutServiceImpl
回调里面的逻辑只是简单的写了点,可以自行丰富起来,比如判断金额、币种等
package com.ratta.service.impl;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import com.paypal.http.HttpResponse;
import com.paypal.http.exceptions.SerializeException;
import com.paypal.http.serializer.Json;
import com.paypal.orders.AddressPortable;
import com.paypal.orders.AmountBreakdown;
import com.paypal.orders.AmountWithBreakdown;
import com.paypal.orders.ApplicationContext;
import com.paypal.orders.Capture;
import com.paypal.orders.Item;
import com.paypal.orders.LinkDescription;
import com.paypal.payments.Refund;
import com.paypal.payments.RefundRequest;
import com.paypal.payments.RefundsGetRequest;
import com.paypal.orders.Money;
import com.paypal.orders.Name;
import com.paypal.orders.Order;
import com.paypal.orders.OrderRequest;
import com.paypal.orders.OrdersCaptureRequest;
import com.paypal.orders.OrdersCreateRequest;
import com.paypal.orders.OrdersGetRequest;
import com.paypal.orders.Payer;
import com.paypal.orders.PurchaseUnit;
import com.paypal.orders.PurchaseUnitRequest;
import com.paypal.orders.ShippingDetail;
import com.paypal.payments.CapturesGetRequest;
import com.paypal.payments.CapturesRefundRequest;
import com.ratta.constants.PayPalCheckoutConstant;
import com.ratta.pay.info.PayPalClient;
import com.ratta.service.PayPalCheckoutService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@RefreshScope
public class PayPalCheckoutServiceImpl implements PayPalCheckoutService {
@Value("${paypal.receiver.email}")
private String receiverEmail;
@Override
public String callback(@SuppressWarnings("rawtypes") Map map) {
log.info(map.toString());
String outTradeNo = (String)map.get("invoice");
String paymentStatus = (String)map.get("payment_status");
String amount = (String)map.get("mc_gross");
String currency = (String)map.get("mc_currency");
String paymentId = (String)map.get("txn_id");
String parentPaymentId = (String)map.get("parent_txn_id");
log.info("商家订单号 = {}", outTradeNo);
log.info("订单状态 = {}", paymentStatus);
log.info("金额 = {}", amount);
log.info("币种 = {}", currency);
log.info("流水号 = {}", paymentId);
log.info("父流水号 = {}", parentPaymentId);
if (!receiverEmail.equals((String) map.get("receiver_email"))) {
log.info("FAIL = 商户id错误, outTradeNo = {}", outTradeNo);
return "failure";
}
if("Completed".equals(paymentStatus)) {
//进行数据库操作
//
//
log.info("支付成功,状态为=COMPLETED");
return "success";
}
if("Refunded".equals(paymentStatus)) {
//进行数据库操作
//
//
log.info("退款成功,发送消息成功");
return "success";
}
if("Pending".equals(paymentStatus) && StringUtils.isEmpty(parentPaymentId)) {
CapturesGetRequest request = new CapturesGetRequest(paymentId);
PayPalClient payPalClient = new PayPalClient();
HttpResponse<com.paypal.payments.Capture> response = null;
try {
response = payPalClient.client(mode, clientId, clientSecret).execute(request);
} catch (IOException e) {
try {
log.error("第1次调用paypal扣款状态查询失败");
response = payPalClient.client(mode, clientId, clientSecret).execute(request);
} catch (Exception e1) {
try {
log.error("第2次调用paypal扣款状态查询失败");
response = payPalClient.client(mode, clientId, clientSecret).execute(request);
} catch (Exception e2) {
log.error("第3次调用paypal扣款状态查询失败,失败原因 {}", e2.getMessage());
}
}
}
log.info("Status Code = {}, Status = {}, CaptureID = {}", response.statusCode(), response.result().status(), response.result().id());
//进行数据库操作
//
//
log.info("该笔订单触发了人工审核,状态为=PENDING,请及时处理");
return "success";
}
return "failure";
}
}
有关如何申请PayPal商家账号,以及测试账号,请参考这篇文章,我就不在过多说明了,因为网上的大多数关于PayPal支付的接入都会提到这个,这个是v1版本的: 最详细的 paypal 支付接口开发–Java版.
最后在说明一点,国内PayPal支付,你可以进入收银台,但支付的时候老是会超时,必须连接vpn才能支付成功。
本人QQ:531719994 欢迎交流
转载:https://blog.csdn.net/qq_36341832/article/details/106334844