|
@@ -0,0 +1,318 @@
|
|
|
|
+package com.fdkankan.pay.util.paypal.sdk;
|
|
|
|
+
|
|
|
|
+import com.alibaba.fastjson.JSONArray;
|
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
|
+import com.fdkankan.pay.common.ResultCode;
|
|
|
|
+import com.fdkankan.pay.entity.Order;
|
|
|
|
+import com.fdkankan.pay.entity.PaypalConfig;
|
|
|
|
+import com.fdkankan.pay.entity.WxConfig;
|
|
|
|
+import com.fdkankan.pay.exception.BusinessException;
|
|
|
|
+import com.fdkankan.pay.service.IPaypalConfigService;
|
|
|
|
+import com.fdkankan.pay.util.CacheUtil;
|
|
|
|
+import com.fdkankan.pay.util.UrlUtils;
|
|
|
|
+import com.paypal.api.payments.*;
|
|
|
|
+import com.paypal.base.codec.binary.Base64;
|
|
|
|
+import com.paypal.base.rest.APIContext;
|
|
|
|
+import com.paypal.base.rest.PayPalRESTException;
|
|
|
|
+import lombok.extern.log4j.Log4j2;
|
|
|
|
+import org.apache.commons.lang.StringUtils;
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
|
+
|
|
|
|
+import java.io.BufferedReader;
|
|
|
|
+import java.io.IOException;
|
|
|
|
+import java.io.InputStreamReader;
|
|
|
|
+import java.math.BigDecimal;
|
|
|
|
+import java.net.HttpURLConnection;
|
|
|
|
+import java.net.URL;
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
+import java.util.HashMap;
|
|
|
|
+import java.util.List;
|
|
|
|
+import java.util.Map;
|
|
|
|
+
|
|
|
|
+@Log4j2
|
|
|
|
+@Service
|
|
|
|
+public class PaypalService {
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ IPaypalConfigService paypalConfigService;
|
|
|
|
+
|
|
|
|
+ public Object openPay(Order param, String ip) throws Exception {
|
|
|
|
+
|
|
|
|
+ String orderSn = param.getOrderSn();
|
|
|
|
+ BigDecimal amount = param.getOrderMoney();
|
|
|
|
+ String subject = param.getOrderType();
|
|
|
|
+ PaypalConfig paypalConfig = paypalConfigService.getByServeId(param.getServeId());
|
|
|
|
+ if(paypalConfig == null){
|
|
|
|
+ throw new BusinessException(ResultCode.PAYPAL_CONFIG_ERROR);
|
|
|
|
+ }
|
|
|
|
+ paypalConfig.setCallBackUrl( CacheUtil.mainUrl + paypalConfig.getCallBackUrl() +"/"+orderSn);
|
|
|
|
+ String body = subject;
|
|
|
|
+ //paypal支付
|
|
|
|
+ if(param.getPayType() == 5){
|
|
|
|
+ return this.payPalPay(orderSn, body, amount,paypalConfig);
|
|
|
|
+ }
|
|
|
|
+ throw new BusinessException(ResultCode.WX_ORDER_PAY_TYPE_ERROR);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ public JSONObject payPalPay(String orderSn, String body, BigDecimal totalFee, PaypalConfig paypalConfig) throws Exception {
|
|
|
|
+ PayPalmentEx payPalmentEx = new PayPalmentEx();
|
|
|
|
+ payPalmentEx.setOrderSn(orderSn);
|
|
|
|
+ payPalmentEx.setOrderTotal(totalFee);
|
|
|
|
+ payPalmentEx.setSubTotal(totalFee);
|
|
|
|
+ payPalmentEx.setDescription(body);
|
|
|
|
+
|
|
|
|
+ Payment payment = this.createPayment(payPalmentEx,paypalConfig);
|
|
|
|
+ for(Links links : payment.getLinks()){
|
|
|
|
+ if(links.getRel().equals("approval_url")){
|
|
|
|
+ // 客户付款登陆地址
|
|
|
|
+ JSONObject j = new JSONObject();
|
|
|
|
+ j.put("redirect", links.getHref());
|
|
|
|
+ j.put("price", totalFee);
|
|
|
|
+ return j;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ throw new BusinessException(ResultCode.PAYPAL_ERROR);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 创建支付
|
|
|
|
+ *
|
|
|
|
+ * @param paymentEx
|
|
|
|
+ * @return
|
|
|
|
+ * @throws PayPalRESTException
|
|
|
|
+ */
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
+ public Payment createPayment(PayPalmentEx paymentEx, PaypalConfig paypalConfig) throws PayPalRESTException {
|
|
|
|
+ Transaction transaction = new Transaction();
|
|
|
|
+ transaction.setDescription(paymentEx.getDescription());
|
|
|
|
+ // 将我们的订单ID保存到支付信息中,用于后面支付回传
|
|
|
|
+ if (null != paymentEx.getOrderSn()) {
|
|
|
|
+ transaction.setCustom(paymentEx.getOrderSn());
|
|
|
|
+ }
|
|
|
|
+ //订单价格
|
|
|
|
+ Amount amount = new Amount();
|
|
|
|
+ amount.setCurrency(paypalConfig.getCurrency());
|
|
|
|
+ // 支付的总价,paypal会校验 total = subTotal + tax + ...
|
|
|
|
+ amount.setTotal(paymentEx.getOrderTotal().toString());
|
|
|
|
+ // 设置各种费用
|
|
|
|
+ Details details = new Details();
|
|
|
|
+ // 商品总价
|
|
|
|
+ if (paymentEx.getSubTotal() != null) {
|
|
|
|
+ details.setSubtotal(paymentEx.getSubTotal().toString());
|
|
|
|
+ }
|
|
|
|
+ // 税费
|
|
|
|
+ if (paymentEx.getTax() != null) {
|
|
|
|
+ details.setTax(paymentEx.getTax().toString());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ amount.setDetails(details);
|
|
|
|
+
|
|
|
|
+ transaction.setAmount(amount);
|
|
|
|
+
|
|
|
|
+ ItemList itemList = new ItemList();
|
|
|
|
+ // 收货地址
|
|
|
|
+ PaypalOrderAddressEx orderAddress = paymentEx.getPaypayOrderAddressEx();
|
|
|
|
+ if (orderAddress != null) {
|
|
|
|
+ ShippingAddress shippingAddress = new ShippingAddress();
|
|
|
|
+ if (StringUtils.isNotEmpty(orderAddress.getFirstName()) && StringUtils.isNotEmpty(orderAddress.getLastName())) {
|
|
|
|
+ shippingAddress.setRecipientName(orderAddress.getFirstName() + "." + orderAddress.getLastName());
|
|
|
|
+ }
|
|
|
|
+ shippingAddress.setCountryCode(orderAddress.getCountryCode());
|
|
|
|
+ shippingAddress.setState(orderAddress.getState());
|
|
|
|
+ shippingAddress.setCity(orderAddress.getCity());
|
|
|
|
+ shippingAddress.setLine1(orderAddress.getAddress());
|
|
|
|
+ shippingAddress.setPhone(orderAddress.getPhone());
|
|
|
|
+ shippingAddress.setPostalCode(orderAddress.getPostalCode());
|
|
|
|
+
|
|
|
|
+ itemList.setShippingAddress(shippingAddress);
|
|
|
|
+ itemList.setShippingPhoneNumber(orderAddress.getPhone());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 商品明细
|
|
|
|
+ List<PaypalOrderItemEx> orderItemList = paymentEx.getPaypalOrderItemExList();
|
|
|
|
+ if (orderItemList != null && orderItemList.size() > 0) {
|
|
|
|
+ List<Item> items = new ArrayList<>();
|
|
|
|
+ for (PaypalOrderItemEx orderItemEx : orderItemList) {
|
|
|
|
+ Item item = new Item();
|
|
|
|
+ item.setSku(orderItemEx.getSku());
|
|
|
|
+ item.setName(orderItemEx.getName());
|
|
|
|
+ item.setPrice(orderItemEx.getPrice().toString());
|
|
|
|
+ item.setQuantity(String.valueOf(orderItemEx.getQuantity()));
|
|
|
|
+ item.setCurrency(paypalConfig.getCurrency());
|
|
|
|
+ items.add(item);
|
|
|
|
+ }
|
|
|
|
+ itemList.setItems(items);
|
|
|
|
+ transaction.setItemList(itemList);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ List<Transaction> transactions = new ArrayList<>();
|
|
|
|
+ transactions.add(transaction);
|
|
|
|
+
|
|
|
|
+ // 支付信息
|
|
|
|
+ Payer payer = new Payer();
|
|
|
|
+ payer.setPaymentMethod(PaypalPaymentMethod.paypal.toString());
|
|
|
|
+
|
|
|
|
+ Payment payment = new Payment();
|
|
|
|
+ //刷新accessToken时间;
|
|
|
|
+ APIContext apiContext = new APIContext("Bearer " + getAccessToken(paypalConfig));
|
|
|
|
+ Map<String, String> sdkConfig = new HashMap<>();
|
|
|
|
+ sdkConfig.put("mode", paypalConfig.getMode());
|
|
|
|
+ apiContext.setConfigurationMap(sdkConfig);
|
|
|
|
+
|
|
|
|
+ payment.setIntent(PaypalPaymentIntent.sale.toString());
|
|
|
|
+ payment.setPayer(payer);
|
|
|
|
+ payment.setTransactions(transactions);
|
|
|
|
+
|
|
|
|
+ //回调地址
|
|
|
|
+ RedirectUrls redirectUrls = new RedirectUrls();
|
|
|
|
+ redirectUrls.setReturnUrl(paypalConfig.getCallBackUrl());
|
|
|
|
+ redirectUrls.setCancelUrl(paypalConfig.getCallBackUrl());
|
|
|
|
+ payment.setRedirectUrls(redirectUrls);
|
|
|
|
+
|
|
|
|
+ return payment.create(apiContext);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 执行支付
|
|
|
|
+ *
|
|
|
|
+ * @param paymentId
|
|
|
|
+ * @param payerId
|
|
|
|
+ * @return
|
|
|
|
+ * @throws PayPalRESTException
|
|
|
|
+ */
|
|
|
|
+ public Payment executePayment(String paymentId, String payerId,PaypalConfig paypalConfig) throws PayPalRESTException {
|
|
|
|
+ Payment payment = new Payment();
|
|
|
|
+ payment.setId(paymentId);
|
|
|
|
+ PaymentExecution paymentExecute = new PaymentExecution();
|
|
|
|
+ paymentExecute.setPayerId(payerId);
|
|
|
|
+
|
|
|
|
+ APIContext apiContext = new APIContext("Bearer " + getAccessToken(paypalConfig));
|
|
|
|
+ Map<String, String> sdkConfig = new HashMap<>();
|
|
|
|
+ sdkConfig.put("mode", paypalConfig.getMode());
|
|
|
|
+ apiContext.setConfigurationMap(sdkConfig);
|
|
|
|
+
|
|
|
|
+ return payment.execute(apiContext, paymentExecute);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取token
|
|
|
|
+ * 了解更多:https://developer.paypal.com/webapps/developer/docs/integration/mobile/verify-mobile-payment/
|
|
|
|
+ * @return
|
|
|
|
+ */
|
|
|
|
+ private String getAccessToken(PaypalConfig paypalConfig) {
|
|
|
|
+ BufferedReader reader = null;
|
|
|
|
+ try {
|
|
|
|
+ URL url = new URL(UrlUtils.TOKEN_URL);
|
|
|
|
+ String authorization = paypalConfig.getClientId() + ":" + paypalConfig.getSecret();
|
|
|
|
+ authorization = Base64.encodeBase64String(authorization.getBytes());
|
|
|
|
+
|
|
|
|
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
|
|
|
+ conn.setRequestMethod("POST");// 提交模式
|
|
|
|
+ //设置请求头header
|
|
|
|
+ conn.setRequestProperty("Accept", "application/json");
|
|
|
|
+ conn.setRequestProperty("Accept-Language", "en_US");
|
|
|
|
+ conn.setRequestProperty("Authorization", "Basic " + authorization);
|
|
|
|
+ // conn.setConnectTimeout(10000);//连接超时 单位毫秒
|
|
|
|
+ // conn.setReadTimeout(2000);//读取超时 单位毫秒
|
|
|
|
+ conn.setDoOutput(true);// 是否输入参数
|
|
|
|
+ String params = "grant_type=client_credentials";
|
|
|
|
+ conn.getOutputStream().write(params.getBytes());// 输入参数
|
|
|
|
+
|
|
|
|
+ InputStreamReader inStream = new InputStreamReader(conn.getInputStream());
|
|
|
|
+ reader = new BufferedReader(inStream);
|
|
|
|
+ StringBuilder result = new StringBuilder();
|
|
|
|
+ String lineTxt = null;
|
|
|
|
+ while ((lineTxt = reader.readLine()) != null) {
|
|
|
|
+ result.append(lineTxt);
|
|
|
|
+ }
|
|
|
|
+ reader.close();
|
|
|
|
+ String accessTokey = JSONObject.parseObject(result.toString()).getString("access_token");
|
|
|
|
+ log.info("getAccessToken:" + accessTokey);
|
|
|
|
+ return accessTokey;
|
|
|
|
+ } catch (Exception err) {
|
|
|
|
+ err.printStackTrace();
|
|
|
|
+ } finally {
|
|
|
|
+ if (reader != null){
|
|
|
|
+ try {
|
|
|
|
+ reader.close();
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
+ e.printStackTrace();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public String getPaymentDetails(String paymentId,PaypalConfig paypalConfig) {
|
|
|
|
+ BufferedReader reader = null;
|
|
|
|
+ try {
|
|
|
|
+ URL url = new URL(UrlUtils.PAYMENT_DETAIL + paymentId);
|
|
|
|
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
|
|
|
+ conn.setRequestMethod("GET");// 提交模式
|
|
|
|
+ //设置请求头header
|
|
|
|
+ conn.setRequestProperty("Accept", "application/json");
|
|
|
|
+ conn.setRequestProperty("Authorization", "Bearer " + getAccessToken(paypalConfig));
|
|
|
|
+ // conn.setConnectTimeout(10000);//连接超时 单位毫秒
|
|
|
|
+ // conn.setReadTimeout(2000);//读取超时 单位毫秒
|
|
|
|
+ InputStreamReader inStream = new InputStreamReader(conn.getInputStream());
|
|
|
|
+ reader = new BufferedReader(inStream);
|
|
|
|
+ StringBuilder result = new StringBuilder();
|
|
|
|
+ String lineTxt = null;
|
|
|
|
+ while ((lineTxt = reader.readLine()) != null) {
|
|
|
|
+ result.append(lineTxt);
|
|
|
|
+ }
|
|
|
|
+ reader.close();
|
|
|
|
+ return result.toString();
|
|
|
|
+ } catch (Exception err) {
|
|
|
|
+ err.printStackTrace();
|
|
|
|
+ }finally {
|
|
|
|
+ if (reader != null){
|
|
|
|
+ try {
|
|
|
|
+ reader.close();
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
+ e.printStackTrace();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public boolean verifyPayment(String paymentId,PaypalConfig paypalConfig){
|
|
|
|
+ //从PayPal获取付款详情
|
|
|
|
+ String str = getPaymentDetails(paymentId,paypalConfig);
|
|
|
|
+ JSONObject detail = JSONObject.parseObject(str);
|
|
|
|
+ //校验订单是否完成
|
|
|
|
+ if("approved".equals(detail.getString("state"))){
|
|
|
|
+ JSONObject transactions = detail.getJSONArray("transactions").getJSONObject(0);
|
|
|
|
+ JSONObject amount = transactions.getJSONObject("amount");
|
|
|
|
+ JSONArray relatedResources = transactions.getJSONArray("related_resources");
|
|
|
|
+ //从数据库查询总金额与Paypal校验支付总金额
|
|
|
|
+ double total = 0;
|
|
|
|
+ if( total != amount.getDouble("total") ){
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ //校验交易货币类型
|
|
|
|
+ String currency = "USD";
|
|
|
|
+ if( !currency.equals(amount.getString("currency")) ){
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ //校验每个子订单是否完成
|
|
|
|
+ for (int i = 0,j = relatedResources.size(); i < j; i++) {
|
|
|
|
+ JSONObject sale = relatedResources.getJSONObject(i).getJSONObject("sale");
|
|
|
|
+ if( !"completed".equals(sale.getString("state")) ){
|
|
|
|
+ System.out.println("子订单未完成,订单状态:"+sale.getString("state"));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+}
|