package com.cyksj.service.paypal; import com.cyksj.common.exception.BusinessRuntimeException; import com.cyksj.common.task.GlobalThreadPoolTaskExecutor; import com.cyksj.common.util.Jsons; import com.cyksj.enums.GatewayApiCode; import com.cyksj.model.entity.OrderDon; import com.cyksj.model.entity.PaypalPayConfig; import com.cyksj.model.request.OrderRefundReq; import com.cyksj.redis.RedisService; import com.cyksj.service.order.OrderDonService; import com.paypal.api.payments.*; import com.paypal.api.payments.Currency; import com.paypal.base.rest.APIContext; import com.paypal.base.rest.OAuthTokenCredential; import com.paypal.base.rest.PayPalRESTException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.io.InputStream; import java.io.OutputStreamWriter; import java.math.BigDecimal; import java.math.RoundingMode; import java.net.HttpURLConnection; import java.net.URL; import java.util.*; /** * @author chan * @date 2021/3/26 11:03 上午 */ @Service @Slf4j @RequiredArgsConstructor public class PayPalService{ private static final GlobalThreadPoolTaskExecutor THREAD_POOL = GlobalThreadPoolTaskExecutor.getInstance(); private final OrderDonService orderDonService; private final PaypalPayConfigService paypalPayConfigService; private final RedisService redisService; private static final String SANDBOX_URL = "https://api-m.sandbox.paypal.com"; private static final String LIVE_URL = "https://api-m.paypal.com"; public String successPayment(String paymentId, String payerId,PaypalPayConfig paypalPayConfig) throws Exception { log.info("paypal回调 paymentId:{},payerId:{}",paymentId,payerId); Payment payment = null; try { payment = this.executePayment(paymentId,payerId,paypalPayConfig.getClientId(),paypalPayConfig.getClientSecret(),paypalPayConfig.getMode()); } catch (PayPalRESTException e) { log.error("支付回调失败",e); payment = Payment.get(getAPIContext(paypalPayConfig.getClientId(),paypalPayConfig.getClientSecret(),paypalPayConfig.getMode()), paymentId); } PayerInfo payerInfo = payment.getPayer().getPayerInfo(); log.info("paypal 支付回调 用户信息:{}", payerInfo); //支付成功 if("approved".equals(payment.getState())){ //paypal 交易id Sale sale = payment.getTransactions().get(0).getRelatedResources().get(0).getSale(); log.info("paypal 支付回调 订单信息:{}", sale); Currency transactionFee = sale.getTransactionFee(); String transationId = sale.getId(); String orderId = payment.getTransactions().get(0).getCustom(); //更新订单支付状态 OrderDon orderDon = orderDonService.getById(orderId); orderDon.setTransactionId(transationId); orderDon.setPayTime(new Date()); orderDon.setAbroadTransactionFee(new BigDecimal(transactionFee.getValue()).multiply(BigDecimal.valueOf(100))); orderDonService.orderNotify(orderDon); log.info("支付成功订单:"+orderId+",支付交易号:"+transationId); log.info("支付成功订单:"+orderId+",返回参数:"+Jsons.toJson(payment)); THREAD_POOL.execute(()->{ try { APIContext apiContext = this.getAPIContext(paypalPayConfig.getClientId(), paypalPayConfig.getClientSecret(), paypalPayConfig.getMode()); URL url = new URL("live".equals(paypalPayConfig.getMode()) ? LIVE_URL : SANDBOX_URL + "/v1/shipping/trackers-batch"); HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); httpConn.setRequestMethod("POST"); httpConn.setRequestProperty("Content-Type", "application/json"); httpConn.setRequestProperty("Authorization", apiContext.getAccessToken()); httpConn.setDoOutput(true); OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream()); writer.write("{\"trackers\": [{ \"transaction_id\": \"" + transationId + "\", \"status\": \"SHIPPED\"}]}"); 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() : ""; log.info("添加物流信息 response:{}",response); } catch (Exception e) { e.printStackTrace(); } }); return redisService.getStr(RedisService.key.ABROAD_SUCCESS_URL.getName() + orderId); } log.info("支付失败返回参数:"+ Jsons.toJson(payment)); return paypalPayConfig.getCancelUrl(); } public Payment createPayment(Long orderId,String successUrl) throws PayPalRESTException { OrderDon orderDon = orderDonService.getById(orderId); //paypal 不能购买奈飞月付账号 24-1-23 if (orderDon.getSkuId() != null && orderDon.getSkuId() == 4) { throw BusinessRuntimeException.getInstance("暂不支持paypal购买该规格车票.."); } redisService.set(RedisService.key.ABROAD_SUCCESS_URL.getName() + orderId,successUrl,RedisService.key.ABROAD_SUCCESS_URL.getTimeout()); BigDecimal money = orderDon.getMoney().divide(new BigDecimal(7),2,RoundingMode.HALF_DOWN); orderDon.setAbroadMoney(money); orderDon.setType(OrderDon.Type.PAYPAL); orderDonService.updateById(orderDon); if (OrderDon.Status.hasPayment.equals(orderDon.getStatus())) { throw new BusinessRuntimeException(GatewayApiCode.METADATA_SYSTEM_ERROR.getCode(),"该捐款已支付"); } PaypalPayConfig paypalPayConfig = paypalPayConfigService.getById(1); // Set payer details Payer payer = new Payer(); payer.setPaymentMethod("paypal"); // Set redirect URLs RedirectUrls redirectUrls = new RedirectUrls(); redirectUrls.setCancelUrl(paypalPayConfig.getCancelUrl()); String returnUrl = "https://" + paypalPayConfig.getHost() + "/8081/api/applets/order/paypal/notify"; //成功回调地址(自己写的/pay/success) redirectUrls.setReturnUrl(returnUrl); Transaction transaction = new Transaction(); transaction.setDescription(""); transaction.setCustom(orderDon.getId().toString()); //订单价格 Amount amount = new Amount(); amount.setCurrency("USD"); // 支付的总价,paypal会校验 total = subTotal + tax + ... BigDecimal totalMoney = money.divide(new BigDecimal(100), 2, RoundingMode.HALF_DOWN); amount.setTotal(totalMoney.toString()); // 设置各种费用 Details details = new Details(); //商品总价 details.setSubtotal(totalMoney.toString()); amount.setDetails(details); transaction.setAmount(amount); // Add transaction to a list List transactions = new ArrayList(); transactions.add(transaction); // Add payment details Payment payment = new Payment(); payment.setIntent("sale"); payment.setPayer(payer); payment.setRedirectUrls(redirectUrls); payment.setTransactions(transactions); return payment.create(this.getAPIContext(paypalPayConfig.getClientId(),paypalPayConfig.getClientSecret(),paypalPayConfig.getMode())); } private APIContext getAPIContext(String clientId,String clientSecret,String mode) throws PayPalRESTException { Map sdkConfig = new HashMap<>(); sdkConfig.put("mode", mode); OAuthTokenCredential authTokenCredential = new OAuthTokenCredential(clientId,clientSecret,sdkConfig); APIContext apiContext = new APIContext(authTokenCredential.getAccessToken()); apiContext.setConfigurationMap(sdkConfig); return apiContext; } /** * 执行支付 */ public Payment executePayment(String paymentId, String PayerID,String clientId,String clientSecret,String mode) throws PayPalRESTException { Payment payment = new Payment(); payment.setId(paymentId); PaymentExecution paymentExecute = new PaymentExecution(); paymentExecute.setPayerId(PayerID); return payment.execute(this.getAPIContext(clientId,clientSecret,mode), paymentExecute); } public Boolean refund(OrderRefundReq refundReq, OrderDon orderDon) throws PayPalRESTException { BigDecimal net = refundReq.getRefundMoney().divide(new BigDecimal(7),2,RoundingMode.HALF_DOWN).subtract(orderDon.getAbroadTransactionFee());; log.info("paypal return order:{},money:{}",orderDon.getId(),net); PaypalPayConfig paypalPayConfig = paypalPayConfigService.getById(1); APIContext apiContext = getAPIContext(paypalPayConfig.getClientId(), paypalPayConfig.getClientSecret(),paypalPayConfig.getMode()); Sale sale = Sale.get(apiContext, orderDon.getTransactionId()); Refund refund = new Refund(); refund.setAmount(new Amount("USD", net.divide(new BigDecimal(100),2,RoundingMode.HALF_DOWN).toString())); refund = sale.refund(apiContext, refund); log.info("paypal return refund:{}",refund); if ("completed".equals(refund.getState())){ orderDon.setStatus(OrderDon.Status.refund); orderDonService.updateById(orderDon); return true; } return false; } }