官方提供的apihttps://developer.paypal.com/docs/?countries=C2

PayPal有v1、v2两个版本的SDK

v1支付请求步骤

1 请求三方paypal接口,获取tokenid。

2 获取token成功之后,会回调预先设置好的returnUrl方法,并带上tokenId和paryId进行token验证

3 同样在returnUrl方法中,验证token通过之后再次调用paypal三方触发支付

4 ipn回调通知(通知支付结果,防止paypal支付成功因为网络原因未返回支付成功的状态,以防万一)

官方推荐接入v2版本,这里以v2版本为例。

v2支付请求步骤

1 请求三方paypal接口,创建订单,返回订单信息(需要把orderId保存起来)

2 创建订单成功之后会跳转到,请求订单时设置的returnUrl地址方法(然后处理扣款支付操作)

(注释,如在paypal checkout 界面取消订单,则直接跳转到cancelUrl方法,通常直接返回到用户下单地址即可,不作任何业务处理)

3 执行扣款支付操作CaptureOrder,调用官方提供的接口

<dependency><groupId>com.paypal.sdk</groupId><artifactId>checkout-sdk</artifactId><version>1.0.2</version></dependency>

4  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();}
}

5. 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")// value = itemTotal + shipping + handling + taxTotal + shippingDiscount;.amountBreakdown(new AmountBreakdown().itemTotal(new Money().currencyCode("USD").value("220.00")) // itemTotal = Item[Supernote A6](value × quantity) + Item[帆布封套](value × quantity).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) {System.out.println("Status Code: " + response.statusCode());System.out.println("Status: " + response.result().status());//需要进行扣款的orderIdSystem.out.println("Order ID: " + response.result().id());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();}}
}

6. 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("invoice_id: {}", capture.invoiceId());if("COMPLETED".equals(capture.status())) {//进行数据库操作,修改订单状态为已支付成功,尽快发货(配合回调和CapturesGet查询确定成功)log.info("支付成功,状态为=COMPLETED");}if("PENDING".equals(capture.status())) {log.info("status_details: {}", capture.captureStatusDetails().reason());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();}}}

7. 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();}}}

8. 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();}}}

9. 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();}}
}

10. 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("退款id RefundOrder生成");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账号,选择账户设置——>选择通知——>选择及时付款通知——>点击更新——>填写你的回调地址路径,开启并保存

11.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 {@Autowiredprivate PayPalCheckoutService payPalCheckoutService;@ApiOperation(value = "ipn异步回调")@PostMapping(value = "/paypal/ipn/back")public String callback(HttpServletRequest request, HttpServletResponse response) {return payPalCheckoutService.callback(RequestToMapUtil.getParameterMap(request));}
}

12.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) {// 参数MapMap properties = request.getParameterMap();// 返回值MapMap 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;}
}

13.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);
}

14.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;@Overridepublic 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)) {String pendingReason = String.valueOf(map.get("pending_reason"));//进行数据库操作////log.info("订单支付成功,状态为=PENDING,产生此状态的原因是 {}", pendingReason );return "success";}if(StringUtils.isEmpty(parentPaymentId)) {if(PayPalCheckoutConstant.PAYMENT_STATUS_REVERSED.equals(paymentStatus)|| PayPalCheckoutConstant.PAYMENT_STATUS_CANCELED_REVERSAL.equals(paymentStatus)|| PayPalCheckoutConstant.PAYMENT_STATUS_DENIED.equals(paymentStatus)) {String reasonCode = String.valueOf(map.get("reason_code"));//进行数据库操作(状态修改)////log.info("订单异常,请尽快查看处理,状态为={},产生此状态的原因是 {} ", paymentStatus, reasonCode);return PayPalCheckoutConstant.SUCCESS;}if(PayPalCheckoutConstant.PAYMENT_STATUS_EXPIRED.equals(paymentStatus)|| PayPalCheckoutConstant.PAYMENT_STATUS_CREATED.equals(paymentStatus)|| PayPalCheckoutConstant.PAYMENT_STATUS_FAILED.equals(paymentStatus)|| PayPalCheckoutConstant.PAYMENT_STATUS_PROCESSED.equals(paymentStatus)|| PayPalCheckoutConstant.PAYMENT_STATUS_VOIDED.equals(paymentStatus)) {//进行数据库操作(状态修改)////log.info("其他订单状态,订单异常,请尽快查看处理, 状态={}", paymentStatus);return PayPalCheckoutConstant.SUCCESS;}}return "failure";}
}

15、PayPalCheckoutConstant

package com.ratta.constants;public class PayPalCheckoutConstant {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";/*** 交易异常*/public static final String FAILURE = "failure";/*** 交易成功*/public static final String SUCCESS = "success";/*** ipn回调。支付成功*/public static final String PAYMENT_STATUS_COMPLETED = "Completed";/*** ipn回调。退款成功*/public static final String PAYMENT_STATUS_REFUNDED = "Refunded";/*** ipn回调。待定*/public static final String PAYMENT_STATUS_PENDING = "Pending";/*** ipn回调,付款因退款或其他类型的冲销而被冲销。资金已从您的帐户余额中删除,并退还给买方*/public static final String PAYMENT_STATUS_REVERSED = "Reversed";/***  ipn回调, 撤销已被取消。例如,您赢得了与客户的纠纷,并且撤回的交易资金已退还给您*/public static final String PAYMENT_STATUS_CANCELED_REVERSAL = "Canceled_Reversal";/***  ipn回调,付款被拒绝*/public static final String PAYMENT_STATUS_DENIED = "Denied";/***  ipn回调, 此授权已过期,无法捕获*/public static final String PAYMENT_STATUS_EXPIRED = "Expired";/***  ipn回调,  德国的ELV付款是通过Express Checkout进行的*/public static final String PAYMENT_STATUS_CREATED = "Created";/*** ipn回调, 付款失败。仅当付款是通过您客户的银行帐户进行的。*/public static final String PAYMENT_STATUS_FAILED = "Failed";/***   ipn回调,付款已被接受*/public static final String PAYMENT_STATUS_PROCESSED = "Processed";/***   ipn回调,此授权已失效*/public static final String PAYMENT_STATUS_VOIDED = "Voided";//订单状态/*** 1、支付完成;捕获的付款的资金已记入收款人的PayPal帐户* 2、退款完成;该交易的资金已记入客户的帐户*/public static final String STATE_COMPLETED = "COMPLETED";/*** 部分退款;少于所捕获付款金额的金额已部分退还给付款人。*/public static final String STATE_PARTIALLY_REFUNDED = "PARTIALLY_REFUNDED";/*** 1、支付待定;捕获的付款资金尚未记入收款人的PayPal帐户。有关更多信息请参见status.details。* 2、退款待定;有关更多信息,请参见status_details.reason。*//*** 支付待定:* capture_status_details* reason 枚举* 捕获的付款状态为PENDING或DENIED的原因。可能的值为:* BUYER_COMPLAINT。付款人与贝宝(PayPal)对此捕获的付款提出了争议。* CHARGEBACK。响应于付款人与用于支付此已捕获付款的金融工具的发行人对此已捕获的付款提出异议,已收回的资金被撤回。* ECHECK。由尚未结清的电子支票支付的付款人。* INTERNATIONAL_WITHDRAWAL。访问您的在线帐户。在您的“帐户概览”中,接受并拒绝此笔付款。* OTHER。无法提供其他特定原因。有关此笔付款的更多信息,请在线访问您的帐户或联系PayPal。* PENDING_REVIEW。捕获的付款正在等待人工审核。*(手动收取)RECEIVING_PREFERENCE_MANDATES_MANUAL_ACTION。收款人尚未为其帐户设置适当的接收首选项。有关如何接受或拒绝此付款的更多信息,请在线访问您的帐户。通常在某些情况下提供此原因,例如,当所捕获付款的货币与收款人的主要持有货币不同时。* REFUNDED。收回的资金已退还。* TRANSACTION_APPROVED_AWAITING_FUNDING。付款人必须将这笔付款的资金汇出。通常,此代码适用于手动EFT。* UNILATERAL。收款人没有PayPal帐户。* VERIFICATION_REQUIRED。收款人的PayPal帐户未通过验证。*//*** 退款待定* 退款具有“PENDING”或“FAILED”状态的原因。 可能的值为:* ECHECK。客户的帐户通过尚未结清的eCheck进行注资。*/public static final String STATE_PENDING = "PENDING";/*** 退款;大于或等于此捕获的付款金额的金额已退还给付款人*/public static final String STATE_REFUNDED = "REFUNDED";/*** 支付拒绝*/public static final String STATE_DENIED = "DENIED";/*** 退款失败*/public static final String STATE_FAILED = "FAILED";/*** 争议状态*/public static final String BUYER_COMPLAINT = "BUYER_COMPLAINT";/*** 沙箱环境请求网关地址*/public static final String SANDBOX = "https://api.sandbox.paypal.com";/*** 生产环境请求网关地址*/public static final String LIVE = "https://api.paypal.com";/*** 添加物流信息请求路径*/public static final String ADD_TRACK_URL = "/v1/shipping/trackers-batch";/*** 修改物流信息请求路径*/public static final String UPDATE_TRACK_URL = "/v1/shipping/trackers/";
}

支付宝支付与退款

https://blog.csdn.net/weixin_41500775/article/details/118545003?spm=1001.2014.3001.5501

信用卡支付与退款

https://blog.csdn.net/weixin_41500775/article/details/118549065?spm=1001.2014.3001.5501

微信支付与退款

https://blog.csdn.net/weixin_41500775/article/details/118522793?spm=1001.2014.3001.5501

paypal支付与退款相关推荐

  1. Paypal支付/回调/退款

    一.下载依赖包 composer require "paypal/rest-api-sdk-php:*" 二.发起支付 <?php use PayPal\Api\Payer; ...

  2. Paypal支付的退款refund之getAccessToken

    这里仅赘述Paypal的Restful API curl方式的php 代码: 下面这段是PayPal官方给出的命令行下的获取access_token的curl方式: curl https://api. ...

  3. paypal php 返回_接入 paypal PHP-sdk 支付 / 回调 / 退款全流程

    记录一下接入paypal 全过程 因项目的原因要用到国外的支付和国外的信用卡,查了一下paypal就内置了信用卡付款方式,所以只需要接入paypal就能基本满足项目海外支付的需求. 查了一下文档发现w ...

  4. PayPal支付集成到自己Web网站

    PayPal如何集成到B2C网站? 因为网上实在找不到集成教程,我习惯性的google了一下相关资料,发现关于paypal接口的中文文档并不多,也不详细,只好自己研究,还好paypal上的资料非常齐全 ...

  5. java对接PayPal支付(v2)

    java对接PayPal支付 我们公司最近开通了网上支付功能,国内选择对接支付宝和微信,国外选择对接paypal, 今天我先将paypal对接方式记录下来,后面会记录微信和支付宝(本人比较懒,微信和支 ...

  6. PayPal支付开发(Vue.js -- node.jsKoa2)

    补充一下:想选择Paypal做支付的,慎选,由于网络服务器网速原因访问部分网站网速极其的慢,他的支付网站还设置timeout过时,导致跳转到他的支付页 资源都没全部拉取下来就timeout掉了.有段时 ...

  7. paypal支付、paypal回调(java后端)

    最近项目引入了paypal支付,在此做个记录 一.申请账号 首先到下面的网址申请个paypal账号(个人账号即可) https://www.paypal.com/c2/home 二.开发者页面登录 h ...

  8. 关于微信支付的退款那些事

    关于微信支付的退款那些事 微信支付的退款 需要双向证书 一个是操作人的电脑上  需要安装的证书 以p12为结尾的 另外一个证书是2个,需要放到服务器上 微信支付的退款,在请求接口的时候,会在发起人的电 ...

  9. Android下集成Paypal支付

    近期项目需要研究paypal支付,官网上的指导写的过于复杂,可能是老外的思维和中国人不一样吧.难得是发现下面这篇文章: http://www.androidhive.info/2015/02/andr ...

最新文章

  1. (转)海量数据面试题集锦
  2. 文本信息检索基本知识【转】
  3. JVM调优:栈上分配和线程本地分配TLAB
  4. 电脑学习方法_怎样快速学习和熟悉电脑知识?|从小白到大神的快捷方法
  5. Automatic IE Testing With Python
  6. 拼团功能,开团并发问题,使用数据库行锁方案
  7. ie11浏览器可以下载java吗_解析:WindowsXP系统能否安装IE11浏览器
  8. TensorFlow 2.0简介
  9. 蓝桥杯 算法训练 字符串合并
  10. php 给注册加上限制条件,在注册用户时用两个条件限制,该如何处理
  11. 机器学习入门笔记(一):模型性能评价与选择
  12. android rom 裁剪,Android之调用系统照相机并裁剪
  13. pandas apply lambda_一分钟一个Pandas小技巧(二)
  14. 爬虫:查找自己浏览器headers
  15. java day19【File类、递归】
  16. linux下安装Telnet服务器
  17. python标准正态分布表(scipy.stats)
  18. 使用colab训练faster-rcnn
  19. 【Python游戏】用Python实现一个2048小游戏 | 附带源码
  20. Mysql中,order by + limt的大坑

热门文章

  1. P4799 [CEOI2015 Day2]世界冰球锦标赛(折半搜索)
  2. 什么是看跌期权?是做空吗?
  3. 基于FPGA的分布式拉曼光纤测温控制系统
  4. 长沙医学院英语四级计算机,长沙医学院2018年6月英语四六级考试报名工作的通知...
  5. 我的世界服务器状态图片,我的世界服务器
  6. STM32学习笔记(五)433M无线发射接收模块
  7. android bean是什么,Android 4.3 Jelly Bean究竟更新了什么?
  8. 抢票插件原理科普:攻击软件还是便利助手?
  9. 夺神之权服务器维护,流放之路3月28日夺神之权停服更新详解
  10. {渡一教育}成哥HTML课程干货笔记整--8