首页 > 基础资料 博客日记

【支付】PayPal支付通道 Java对接 (下单 付款 确认 退款 查询 回调)

2024-12-31 04:00:06基础资料围观82

Java资料网推荐【支付】PayPal支付通道 Java对接 (下单 付款 确认 退款 查询 回调)这篇文章给大家,欢迎收藏Java资料网享受知识的乐趣

使用Java 对接 PayPal 接口,从下单,支付,确认订单、查询、退款、退款查询、回调处理等全流程代码示例,以及图文说明。

PayPal接口调用时序图

后台服务 PayPal 【授权】 返回AccessToken,后续每个请求后腰携带Token 【下单】(v2/checkout/orders) 返回订单信息,包含支付链接(links)和订单id 浏览器打开支付链接,输入PayPal账户,【完成支付】 支付完成,跳转到完成页面 【支付成功回调】(webhook请求,CHECKOUT.ORDER.APPROVED事件) 【支付确认】(/v2/checkout/orders/{order_id}/capture) 返回确定信息(得到captureId,退款使用) 【确认回调】(PAYMENT.CAPTURE.COMPLETED事件)修改订单状态置为成功 【查询订单】(/v2/checkout/orders/{order_id}) 返回订单信息 【退款】(/v2/payments/captures/{capture_id}/refund) 返回退款订单信息(退款ID refund_id) 【查询退款】订单(/v2/payments/refunds/{refund_id}) 返回退款订单信息 后台服务 PayPal

PayPal账户注册

PayPal的API接口使用client IDclient secret进行身份认证。

账户注册成功后登录开发者平台(https://developer.paypal.com/),然后选择Apps & Credentials菜单,可以看见平台创建了一个默认应用,包含client IDclient secret 。把它们都复制下来。

1、接口授权,获取AccessToken

curl 格式

curl -v -X POST "https://api-m.sandbox.paypal.com/v1/oauth2/token"\
 -u "CLIENT_ID:CLIENT_SECRET"\
 -H "Content-Type: application/x-www-form-urlencoded"\
 -d "grant_type=client_credentials"

返回示例

{
  "scope": "https://uri.paypal.com/services/invoicing https://uri.paypal.com/services/disputes/read-buyer https://uri.paypal.com/services/payments/realtimepayment https://uri.paypal.com/services/disputes/update-seller https://uri.paypal.com/services/payments/payment/authcapture openid https://uri.paypal.com/services/disputes/read-seller https://uri.paypal.com/services/payments/refund https://api-m.paypal.com/v1/vault/credit-card https://api-m.paypal.com/v1/payments/.* https://uri.paypal.com/payments/payouts https://api-m.paypal.com/v1/vault/credit-card/.* https://uri.paypal.com/services/subscriptions https://uri.paypal.com/services/applications/webhooks",
  "access_token": "A21AAFEpH4PsADK7qSS7pSRsgzfENtu-Q1ysgEDVDESseMHBYXVJYE8ovjj68elIDy8nF26AwPhfXTIeWAZHSLIsQkSYz9ifg",
  "token_type": "Bearer",
  "app_id": "APP-80W284485P519543T",
  "expires_in": 31668,
  "nonce": "2020-04-03T15:35:36ZaYZlGvEkV4yVSz8g6bAKFoGSEzuy3CQcz3ljhibkOHg"
}

java 格式

import java.io.BufferedReader;  
import java.io.InputStreamReader;  
import java.io.OutputStream;  
import java.net.HttpURLConnection;  
import java.net.URL;  
import java.util.Base64;  
  
public class PayPalAccessToken {  
  
    public static void main(String[] args) {  
        try {  
            String clientId = "CLIENT_ID";  
            String clientSecret = "CLIENT_SECRET";  
            String credentials = clientId + ":" + clientSecret;  
            String encodedCredentials = Base64.getEncoder().encodeToString(credentials.getBytes());  
  
            URL url = new URL("https://api-m.sandbox.paypal.com/v1/oauth2/token");  
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();  
            connection.setRequestMethod("POST");  
            connection.setRequestProperty("Authorization", "Basic " + encodedCredentials);  
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");  
            connection.setDoOutput(true);  
  
            String postParameters = "grant_type=client_credentials";  
            try (OutputStream os = connection.getOutputStream()) {  
                byte[] postParametersBytes = postParameters.getBytes();  
                os.write(postParametersBytes);  
                os.flush();  
            }  
  
            int responseCode = connection.getResponseCode();  
            System.out.println("Response Code : " + responseCode);  
  
            BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));  
            String inputLine;  
            StringBuffer response = new StringBuffer();  
  
            while ((inputLine = in.readLine()) != null) {  
                response.append(inputLine);  
            }  
            in.close();  
  
            // 打印结果  
            System.out.println(response.toString());  
  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}

2、下单 v2/checkout/orders

@Test
public void order() throws IOException {
    URL url = new URL("https://api-m.sandbox.paypal.com/v2/checkout/orders");
    HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
    httpConn.setRequestMethod("POST");

    httpConn.setRequestProperty("Content-Type", "application/json");
    httpConn.setRequestProperty("PayPal-Request-Id", "7b92603e-77ed-4896-8e78-5dea2050476kk");
    // 设置 accessToken
    httpConn.setRequestProperty("Authorization", "Bearer A21AAKn5cnLgrxyUuxaQiGeuuA1aQWPZuxyZLBJbXK04-BNm0FJIJZjXef_VFumB9RLCVurmrPh9DXn2KaLwVEau7NqsLTcNA");

    // 金额
    JSONObject amount = new JSONObject();
    amount.put("currency_code", "USD");
    amount.put("value", "10.00");

    JSONObject address = new JSONObject();
    address.put("address_line_1", "虹桥镇");
    address.put("address_line_2", "茅台路168号");
    address.put("admin_area_1", "上海市");
    address.put("admin_area_2", "闵行区");
    address.put("postal_code", "20000");
    address.put("country_code", "CN");

    // 客户姓名
    JSONObject name = new JSONObject();
    name.put("full_name", "Jacky");
    // 客户信息机地址
    JSONObject shipping = new JSONObject();
    shipping.put("name", name);
    shipping.put("address", address);

    // 购买人信息,数组,最多可以同时传10个
    JSONObject purchaseUnit = new JSONObject();
    purchaseUnit.put("reference_id", UUID.randomUUID().toString());
    purchaseUnit.put("custom_id", UUID.randomUUID().toString());
    purchaseUnit.put("amount", amount);
    purchaseUnit.put("description", "测试支付");
    purchaseUnit.put("shipping", shipping);
    JSONArray puprchase_units = new JSONArray();
    puprchase_units.add(purchaseUnit);
    // 订单上下文信息,取消地址、返回地址设置
    JSONObject applicationContext = new JSONObject();
    applicationContext.put("cancel_url", "http://localhost/paypalv2/cancel");
    applicationContext.put("return_url", "http://localhost/paypalv2/back");
    JSONObject json = new JSONObject();
    json.put("intent", "CAPTURE");// 用户付款了,商户立即收款
    json.put("purchase_units", puprchase_units);
    json.put("application_context", applicationContext);
    httpConn.setDoOutput(true);
    OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
    writer.write(json.toJSONString());
    writer.flush();
    writer.close();
    httpConn.getOutputStream().close();

    InputStream responseStream = httpConn.getResponseCode() / 100 == 2
            ? httpConn.getInputStream()
            : httpConn.getErrorStream();
    Scanner s = new Scanner(responseStream).useDelimiter("\\A");
    String response = s.hasNext() ? s.next() : "";
    System.out.println(response);
}

返回示例

返回结果中 ref = approve 的链接就是支付链接。
本例是https://www.sandbox.paypal.com/checkoutnow?token=2JF08781K01251707

在浏览器中直接打开链接就可以支付了。

{
	"id": "2JF08781K01251707",
	"status": "CREATED",
	"links": [{
		"href": "https://api.sandbox.paypal.com/v2/checkout/orders/2JF08781K01251707",
		"rel": "self",
		"method": "GET"
	}, {
		"href": "https://www.sandbox.paypal.com/checkoutnow?token=2JF08781K01251707",
		"rel": "approve",
		"method": "GET"
	}, {
		"href": "https://api.sandbox.paypal.com/v2/checkout/orders/2JF08781K01251707",
		"rel": "update",
		"method": "PATCH"
	}, {
		"href": "https://api.sandbox.paypal.com/v2/checkout/orders/2JF08781K01251707/capture",
		"rel": "capture",
		"method": "POST"
	}]
}

3、付款

打开支付链接,并登录PayPal账户支付

登录账户,sandbox环境的账户有5000美元的余额,直接使用余额支付,点击“继续查看订单”,会返回到下单时传入的return_url

点击继续,页面跳转到return_url

4、确认付款 /v2/checkout/orders/{order_id}/capture

PayPal的订单在付款后,必须调用确认接口后才能到账。可以在回调事件中处理,收到回调事件时直接调用capture接口。

java请求

@Test
public void capture() throws IOException {
    // 传入订单ID作为路径参数
    String api = String.format("https://api-m.sandbox.paypal.com/v2/checkout/orders/%1s/capture","2JF08781K01251707");
    URL url = new URL(api);
    HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
    httpConn.setRequestMethod("POST");

    httpConn.setRequestProperty("Content-Type", "application/json");
    httpConn.setRequestProperty("PayPal-Request-Id", "7b92603e-77ed-4896-8e78-5dea2050476a");
    // 设置 accessToken
    httpConn.setRequestProperty("Authorization", "Bearer A21AAKn5cnLgrxyUuxaQiGeuuA1aQWPZuxyZLBJbXK04-BNm0FJIJZjXef_VFumB9RLCVurmrPh9DXn2KaLwVEau7NqsLTcNA");

    InputStream responseStream = httpConn.getResponseCode() / 100 == 2
            ? httpConn.getInputStream()
            : httpConn.getErrorStream();
    Scanner s = new Scanner(responseStream).useDelimiter("\\A");
    String response = s.hasNext() ? s.next() : "";
    System.out.println(response);
}

返回示例

其中 purchase_units[0]/payments/captures[0]/id 就是 captureId ,记住这个值,在退款时使用。

本例是 9T136542CK5139010

{
	"id": "2JF08781K01251707",
	"status": "COMPLETED",
	"payment_source": {
		"paypal": {
			"email_address": "sb-shn43n32414178@personal.example.com",
			"account_id": "6MFUB6K8A4CMG",
			"account_status": "VERIFIED",
			"name": {
				"given_name": "John",
				"surname": "Doe"
			},
			"address": {
				"country_code": "C2"
			}
		}
	},
	"purchase_units": [{
		"reference_id": "90f571f6-ad20-4e58-83ca-625f41601188",
		"shipping": {
			"name": {
				"full_name": "Jacky"
			},
			"address": {
				"address_line_1": "虹桥镇",
				"address_line_2": "茅台路168号",
				"admin_area_2": "闵行区",
				"admin_area_1": "上海市",
				"postal_code": "20000",
				"country_code": "CN"
			}
		},
		"payments": {
			"captures": [{
				"id": "9T136542CK5139010",
				"status": "COMPLETED",
				"amount": {
					"currency_code": "USD",
					"value": "10.00"
				},
				"final_capture": true,
				"seller_protection": {
					"status": "ELIGIBLE",
					"dispute_categories": ["ITEM_NOT_RECEIVED", "UNAUTHORIZED_TRANSACTION"]
				},
				"seller_receivable_breakdown": {
					"gross_amount": {
						"currency_code": "USD",
						"value": "10.00"
					},
					"paypal_fee": {
						"currency_code": "USD",
						"value": "0.69"
					},
					"net_amount": {
						"currency_code": "USD",
						"value": "9.31"
					},
					"receivable_amount": {
						"currency_code": "HKD",
						"value": "70.76"
					},
					"exchange_rate": {
						"source_currency": "USD",
						"target_currency": "HKD",
						"value": "7.6006905"
					}
				},
				"custom_id": "16803aa5-e2a9-45d5-b00d-7b2919676074",
				"links": [{
					"href": "https://api.sandbox.paypal.com/v2/payments/captures/9T136542CK5139010",
					"rel": "self",
					"method": "GET"
				}, {
					"href": "https://api.sandbox.paypal.com/v2/payments/captures/9T136542CK5139010/refund",
					"rel": "refund",
					"method": "POST"
				}, {
					"href": "https://api.sandbox.paypal.com/v2/checkout/orders/2JF08781K01251707",
					"rel": "up",
					"method": "GET"
				}],
				"create_time": "2024-09-03T06:02:59Z",
				"update_time": "2024-09-03T06:02:59Z"
			}]
		}
	}],
	"payer": {
		"name": {
			"given_name": "John",
			"surname": "Doe"
		},
		"email_address": "sb-shn43n32414178@personal.example.com",
		"payer_id": "6MFUB6K8A4CMG",
		"address": {
			"country_code": "C2"
		}
	},
	"links": [{
		"href": "https://api.sandbox.paypal.com/v2/checkout/orders/2JF08781K01251707",
		"rel": "self",
		"method": "GET"
	}]
}

5、订单查询 /v2/checkout/orders/{order_id}

查询订单详情,如果没有收到回调,可以主动查询订单状态

java请求

@Test
public void query() throws IOException {
    // 传入订单ID作为路径参数
    String api = String.format("https://api-m.sandbox.paypal.com/v2/checkout/orders/%1s","2JF08781K01251707");
    URL url = new URL(api);
    HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
    httpConn.setRequestMethod("GET");

    httpConn.setRequestProperty("Content-Type", "application/json");
    httpConn.setRequestProperty("PayPal-Request-Id", "7b92603e-77ed-4896-8e78-5dea2050476a");
    // 设置 accessToken
    httpConn.setRequestProperty("Authorization", "Bearer A21AAKn5cnLgrxyUuxaQiGeuuA1aQWPZuxyZLBJbXK04-BNm0FJIJZjXef_VFumB9RLCVurmrPh9DXn2KaLwVEau7NqsLTcNA");

    InputStream responseStream = httpConn.getResponseCode() / 100 == 2
            ? httpConn.getInputStream()
            : httpConn.getErrorStream();
    Scanner s = new Scanner(responseStream).useDelimiter("\\A");
    String response = s.hasNext() ? s.next() : "";
    System.out.println(response);
}

返回示例

{
	"id": "2JF08781K01251707",
	"intent": "CAPTURE",
	"status": "COMPLETED",
	"payment_source": {
		"paypal": {
			"email_address": "sb-shn43n32414178@personal.example.com",
			"account_id": "6MFUB6K8A4CMG",
			"account_status": "VERIFIED",
			"name": {
				"given_name": "John",
				"surname": "Doe"
			},
			"address": {
				"country_code": "C2"
			}
		}
	},
	"purchase_units": [{
		"reference_id": "90f571f6-ad20-4e58-83ca-625f41601188",
		"amount": {
			"currency_code": "USD",
			"value": "10.00"
		},
		"payee": {
			"email_address": "sb-candq32416718@business.example.com",
			"merchant_id": "HNKCPAL3PZUUL"
		},
		"description": "测试支付",
		"custom_id": "16803aa5-e2a9-45d5-b00d-7b2919676074",
		"shipping": {
			"name": {
				"full_name": "Jacky"
			},
			"address": {
				"address_line_1": "虹桥镇",
				"address_line_2": "茅台路168号",
				"admin_area_2": "闵行区",
				"admin_area_1": "上海市",
				"postal_code": "20000",
				"country_code": "CN"
			}
		},
		"payments": {
			"captures": [{
				"id": "9T136542CK5139010",
				"status": "COMPLETED",
				"amount": {
					"currency_code": "USD",
					"value": "10.00"
				},
				"final_capture": true,
				"seller_protection": {
					"status": "ELIGIBLE",
					"dispute_categories": ["ITEM_NOT_RECEIVED", "UNAUTHORIZED_TRANSACTION"]
				},
				"seller_receivable_breakdown": {
					"gross_amount": {
						"currency_code": "USD",
						"value": "10.00"
					},
					"paypal_fee": {
						"currency_code": "USD",
						"value": "0.69"
					},
					"net_amount": {
						"currency_code": "USD",
						"value": "9.31"
					},
					"receivable_amount": {
						"currency_code": "HKD",
						"value": "70.76"
					},
					"exchange_rate": {
						"source_currency": "USD",
						"target_currency": "HKD",
						"value": "7.6006905"
					}
				},
				"custom_id": "16803aa5-e2a9-45d5-b00d-7b2919676074",
				"links": [{
					"href": "https://api.sandbox.paypal.com/v2/payments/captures/9T136542CK5139010",
					"rel": "self",
					"method": "GET"
				}, {
					"href": "https://api.sandbox.paypal.com/v2/payments/captures/9T136542CK5139010/refund",
					"rel": "refund",
					"method": "POST"
				}, {
					"href": "https://api.sandbox.paypal.com/v2/checkout/orders/2JF08781K01251707",
					"rel": "up",
					"method": "GET"
				}],
				"create_time": "2024-09-03T06:02:59Z",
				"update_time": "2024-09-03T06:02:59Z"
			}]
		}
	}],
	"payer": {
		"name": {
			"given_name": "John",
			"surname": "Doe"
		},
		"email_address": "sb-shn43n32414178@personal.example.com",
		"payer_id": "6MFUB6K8A4CMG",
		"address": {
			"country_code": "C2"
		}
	},
	"create_time": "2024-09-03T04:09:41Z",
	"update_time": "2024-09-03T06:02:59Z",
	"links": [{
		"href": "https://api.sandbox.paypal.com/v2/checkout/orders/2JF08781K01251707",
		"rel": "self",
		"method": "GET"
	}]
}

6、退款 /v2/payments/captures/{capture_id}/refund

退款请求必须传入 capture_id,该参数在上面支付订单确认时返回,本例是 9T136542CK5139010

Java请求

@Test
public void refund() throws IOException {
    // 传入订单ID作为路径参数
    String api = String.format("https://api-m.sandbox.paypal.com/v2/payments/captures/%1s/refund","9T136542CK5139010");
    URL url = new URL(api);
    HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
    httpConn.setRequestMethod("POST");

    httpConn.setRequestProperty("Content-Type", "application/json");
    httpConn.setRequestProperty("Prefer", "return=representation");
    httpConn.setRequestProperty("PayPal-Request-Id", "7b92603e-77ed-4896-8e78-5dea2050476a");
    // 设置 accessToken
    httpConn.setRequestProperty("Authorization", "Bearer A21AAKn5cnLgrxyUuxaQiGeuuA1aQWPZuxyZLBJbXK04-BNm0FJIJZjXef_VFumB9RLCVurmrPh9DXn2KaLwVEau7NqsLTcNA");
    httpConn.setDoOutput(true);

    JSONObject json = new JSONObject();
    // 传入退款金额
    JSONObject amount = new JSONObject();
    amount.put("value","10.00");
    amount.put("currency_code","USD");
    json.put("amount",amount);
    OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
    writer.write(json.toJSONString());
    writer.flush();
    writer.close();
    httpConn.getOutputStream().close();

    InputStream responseStream = httpConn.getResponseCode() / 100 == 2
            ? httpConn.getInputStream()
            : httpConn.getErrorStream();
    Scanner s = new Scanner(responseStream).useDelimiter("\\A");
    String response = s.hasNext() ? s.next() : "";
    System.out.println(response);
}

返回示例

从结果中取得退款订单ID,用于退款订单查询使用。本例是 73L96122H5742963F

{
	"id": "73L96122H5742963F",
	"amount": {
		"currency_code": "USD",
		"value": "10.00"
	},
	"seller_payable_breakdown": {
		"gross_amount": {
			"currency_code": "USD",
			"value": "10.00"
		},
		"paypal_fee": {
			"currency_code": "USD",
			"value": "0.00"
		},
		"net_amount": {
			"currency_code": "USD",
			"value": "10.00"
		},
		"total_refunded_amount": {
			"currency_code": "USD",
			"value": "10.00"
		}
	},
	"custom_id": "16803aa5-e2a9-45d5-b00d-7b2919676074",
	"status": "COMPLETED",
	"create_time": "2024-09-02T23:27:51-07:00",
	"update_time": "2024-09-02T23:27:51-07:00",
	"links": [{
		"href": "https://api.sandbox.paypal.com/v2/payments/refunds/73L96122H5742963F",
		"rel": "self",
		"method": "GET"
	}, {
		"href": "https://api.sandbox.paypal.com/v2/payments/captures/9T136542CK5139010",
		"rel": "up",
		"method": "GET"
	}]
}

7、退款查询 /v2/payments/refunds/{refund_id}

java请求

@Test
public void refundQuery() throws IOException {
    // 传入退款订单ID作为路径参数
    String api = String.format("https://api-m.sandbox.paypal.com/v2/payments/refunds/%1s","73L96122H5742963F");
    URL url = new URL(api);
    HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
    httpConn.setRequestMethod("GET");

    httpConn.setRequestProperty("Content-Type", "application/json");
    // 设置 accessToken
    httpConn.setRequestProperty("Authorization", "Bearer A21AAKn5cnLgrxyUuxaQiGeuuA1aQWPZuxyZLBJbXK04-BNm0FJIJZjXef_VFumB9RLCVurmrPh9DXn2KaLwVEau7NqsLTcNA");

    InputStream responseStream = httpConn.getResponseCode() / 100 == 2
            ? httpConn.getInputStream()
            : httpConn.getErrorStream();
    Scanner s = new Scanner(responseStream).useDelimiter("\\A");
    String response = s.hasNext() ? s.next() : "";
    System.out.println(response);
}

返回示例

{
	"id": "73L96122H5742963F",
	"amount": {
		"currency_code": "USD",
		"value": "10.00"
	},
	"seller_payable_breakdown": {
		"gross_amount": {
			"currency_code": "USD",
			"value": "10.00"
		},
		"paypal_fee": {
			"currency_code": "USD",
			"value": "0.00"
		},
		"net_amount": {
			"currency_code": "USD",
			"value": "10.00"
		},
		"total_refunded_amount": {
			"currency_code": "USD",
			"value": "10.00"
		}
	},
	"custom_id": "16803aa5-e2a9-45d5-b00d-7b2919676074",
	"status": "COMPLETED",
	"create_time": "2024-09-02T23:27:51-07:00",
	"update_time": "2024-09-02T23:27:51-07:00",
	"payer": {
		"email_address": "sb-candq32416718@business.example.com",
		"merchant_id": "HNKCPAL3PZUUL"
	},
	"links": [{
		"href": "https://api.sandbox.paypal.com/v2/payments/refunds/73L96122H5742963F",
		"rel": "self",
		"method": "GET"
	}, {
		"href": "https://api.sandbox.paypal.com/v2/payments/captures/9T136542CK5139010",
		"rel": "up",
		"method": "GET"
	}]
}

8、回调处理

PayPal的API事件回调是通过webhook请求实现的,首先要在开发者平台上面配置回调地址,以及对应的事件。

登录开发者平台 (https://developer.paypal.com/),点击菜单 “Apps & Credentials”,点击应用-edit,进入应用编辑页面。
页面拖下最下面,可以看见 Sandbox Webhooks ,点击 Add Webhook

输入回调地址(必须是https的请求),以及勾选事件(选择Checkout 以及 Payments & Payouts 即可)


回调时间列表 https://developer.paypal.com/api/rest/webhooks/event-names/

接收Webhook信息

Webhook回调body内容大致如下

{
	"event_version": "1.0",
	"summary": "An order has been approved by buyer",
	"event_type": "CHECKOUT.ORDER.APPROVED",
	"create_time": "2024-09-03T07:22:32.983Z",
	"resource": {
		"create_time": "2024-09-03T07:22:03Z",
		"purchase_units": [{
			"payee": {
				"email_address": "sb-sgxuo32361369@business.example.com",
				"merchant_id": "Q348V8XR4TH2N"
			},
			"amount": {
				"value": "0.01",
				"currency_code": "HKD"
			},
			"reference_id": "default",
			"shipping": {
				"address": {
					"country_code": "CN",
					"admin_area_1": "上海市",
					"address_line_1": "虹桥镇",
					"admin_area_2": "闵行区",
					"address_line_2": "茅台路168号",
					"postal_code": "20000"
				},
				"name": {
					"full_name": "Jacky"
				}
			},
			"custom_id": "f909fb55f28f44e290c50dd048d57266",
			"description": "订单说明"
		}],
		"links": [{
			"method": "GET",
			"rel": "self",
			"href": "https://api.sandbox.paypal.com/v2/checkout/orders/69F56626WL954315U"
		}, {
			"method": "PATCH",
			"rel": "update",
			"href": "https://api.sandbox.paypal.com/v2/checkout/orders/69F56626WL954315U"
		}, {
			"method": "POST",
			"rel": "capture",
			"href": "https://api.sandbox.paypal.com/v2/checkout/orders/69F56626WL954315U/capture"
		}],
		"id": "69F56626WL954315U",
		"payment_source": {
			"paypal": {
				"email_address": "sb-9kb7y32414123@personal.example.com",
				"account_id": "JEDY926LHZCVL",
				"address": {
					"country_code": "C2"
				},
				"name": {
					"surname": "Doe",
					"given_name": "John"
				},
				"account_status": "VERIFIED"
			}
		},
		"intent": "CAPTURE",
		"payer": {
			"email_address": "sb-9kb7y32414123@personal.example.com",
			"address": {
				"country_code": "C2"
			},
			"name": {
				"surname": "Doe",
				"given_name": "John"
			},
			"payer_id": "JEDY926LHZCVL"
		},
		"status": "APPROVED"
	},
	"resource_type": "checkout-order",
	"links": [{
		"method": "GET",
		"rel": "self",
		"href": "https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-8MY579521J6808611-3AB13057V80946128"
	}, {
		"method": "POST",
		"rel": "resend",
		"href": "https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-8MY579521J6808611-3AB13057V80946128/resend"
	}],
	"id": "WH-8MY579521J6808611-3AB13057V80946128",
	"resource_version": "2.0"
}

同时Webhook回调的header中也有很多数据,如下5个数据需要取出,在验证签名时使用:

"paypal-transmission-sig": ["lqd3Wg4zb/VqOu8CcAbKKcw2teLr+80jMoBh8/QdA+lyP3GqF3PB/2AIMTH9mG6AtZy2f8O+79zXvQu5gOYf1Cw77G8ExEcmAdlgozJOdD9Zc4hEYJUVa8i2BQKwjEcU0MhmuMqTFMkq5yErgPi91t0lfodz3yNoE4Fs8U1bN78s/lHNQ9Rtnxf6oizt02MXR+KTIT9iGiE0GkMLKcjoLv0NsAjYP4BxEy3hE64n3kGWix3Cj7vMM1GfX32PXblRAuurbg/VcIl1fbnLTfYgL401BqaEh/LwSAHBD6VRg84xXCEqBjgJpxpIFwlPTO0zNU9jvJLMu+pOd8dokMqcaw=="],
"paypal-cert-url": ["https://api.sandbox.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-ab66f33d"],
"paypal-auth-algo": ["SHA256withRSA"],
"paypal-transmission-id": ["9752bb30-69c5-11ef-8631-b5563cb1cd6c"],
"paypal-transmission-time": ["2024-09-03T07:24:44Z"]

回调信息签名验证

参考 https://developer.paypal.com/docs/api/webhooks/v1/#verify-webhook-signature_post

java代码

@Test
public  void verify() throws IOException {
    URL url = new URL("https://api-m.sandbox.paypal.com/v1/notifications/verify-webhook-signature");
    HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
    httpConn.setRequestMethod("POST");

    httpConn.setRequestProperty("Content-Type", "application/json");
    // 设置 accessToken
    httpConn.setRequestProperty("Authorization", "Bearer A21AAKn5cnLgrxyUuxaQiGeuuA1aQWPZuxyZLBJbXK04-BNm0FJIJZjXef_VFumB9RLCVurmrPh9DXn2KaLwVEau7NqsLTcNA");

    httpConn.setDoOutput(true);
    OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());


    JSONObject param = new JSONObject();
    param.put("auth_algo", "SHA256withRSA");
    param.put("cert_url", "https://api.sandbox.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-ab66f33d");
    param.put("transmission_id", "9752bb30-69c5-11ef-8631-b5563cb1cd6c");
    param.put("transmission_sig", "lqd3Wg4zb/VqOu8CcAbKKcw2teLr+80jMoBh8/QdA+lyP3GqF3PB/2AIMTH9mG6AtZy2f8O+79zXvQu5gOYf1Cw77G8ExEcmAdlgozJOdD9Zc4hEYJUVa8i2BQKwjEcU0MhmuMqTFMkq5yErgPi91t0lfodz3yNoE4Fs8U1bN78s/lHNQ9Rtnxf6oizt02MXR+KTIT9iGiE0GkMLKcjoLv0NsAjYP4BxEy3hE64n3kGWix3Cj7vMM1GfX32PXblRAuurbg/VcIl1fbnLTfYgL401BqaEh/LwSAHBD6VRg84xXCEqBjgJpxpIFwlPTO0zNU9jvJLMu+pOd8dokMqcaw==");
    param.put("transmission_time", "2024-09-03T07:24:44Z");
    // The ID of the webhook as configured in your Developer Portal account.
    param.put("webhook_id", "14H498652P299021K");
    String event_str ="{\"event_version\":\"1.0\",\"summary\":\"Payment completed for HKD 0.01 HKD\",\"event_type\":\"PAYMENT.CAPTURE.COMPLETED\",\"create_time\":\"2024-09-03T07:24:32.968Z\",\"resource\":{\"payee\":{\"email_address\":\"sb-sgxuo32361369@business.example.com\",\"merchant_id\":\"Q348V8XR4TH2N\"},\"amount\":{\"value\":\"0.01\",\"currency_code\":\"HKD\"},\"seller_protection\":{\"dispute_categories\":[\"ITEM_NOT_RECEIVED\",\"UNAUTHORIZED_TRANSACTION\"],\"status\":\"ELIGIBLE\"},\"supplementary_data\":{\"related_ids\":{\"order_id\":\"69F56626WL954315U\"}},\"update_time\":\"2024-09-03T07:24:28Z\",\"create_time\":\"2024-09-03T07:24:28Z\",\"final_capture\":true,\"seller_receivable_breakdown\":{\"paypal_fee\":{\"value\":\"0.01\",\"currency_code\":\"HKD\"},\"gross_amount\":{\"value\":\"0.01\",\"currency_code\":\"HKD\"},\"net_amount\":{\"value\":\"0.00\",\"currency_code\":\"HKD\"}},\"custom_id\":\"f909fb55f28f44e290c50dd048d57266\",\"links\":[{\"method\":\"GET\",\"rel\":\"self\",\"href\":\"https://api.sandbox.paypal.com/v2/payments/captures/6P662124DH1279613\"},{\"method\":\"POST\",\"rel\":\"refund\",\"href\":\"https://api.sandbox.paypal.com/v2/payments/captures/6P662124DH1279613/refund\"},{\"method\":\"GET\",\"rel\":\"up\",\"href\":\"https://api.sandbox.paypal.com/v2/checkout/orders/69F56626WL954315U\"}],\"id\":\"6P662124DH1279613\",\"status\":\"COMPLETED\"},\"resource_type\":\"capture\",\"links\":[{\"method\":\"GET\",\"rel\":\"self\",\"href\":\"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-85V35090TG357611Y-4UY875675G964354R\"},{\"method\":\"POST\",\"rel\":\"resend\",\"href\":\"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-85V35090TG357611Y-4UY875675G964354R/resend\"}],\"id\":\"WH-85V35090TG357611Y-4UY875675G964354R\",\"resource_version\":\"2.0\"}";
    JSONObject jsonObject = JSON.parseObject(event_str);
    param.put("webhook_event", jsonObject);
    writer.write(param.toJSONString());
    writer.flush();
    writer.close();
    httpConn.getOutputStream().close();

    InputStream responseStream = httpConn.getResponseCode() / 100 == 2
            ? httpConn.getInputStream()
            : httpConn.getErrorStream();
    Scanner s = new Scanner(responseStream).useDelimiter("\\A");
    String response = s.hasNext() ? s.next() : "";
    System.out.println(response);
}

返回信息

{"verification_status":"SUCCESS"}

参考


文章来源:https://blog.csdn.net/wlddhj/article/details/141713523
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐

标签云