paypal支付与退款

4 篇文章 1 订阅
1 篇文章 0 订阅

官方提供的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());
            //需要进行扣款的orderId
			System.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 {

	@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));
	}
}

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) {
		// 参数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;
	}
}

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;

	

	@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)) {
			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

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 20
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程治铭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值