package com.cyksj.service.paypal; import com.baomidou.mybatisplus.core.toolkit.Wrappers; 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.OrderMasterDon; 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.cyksj.service.order.OrderMasterDonService; 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 final OrderMasterDonService orderMasterDonService; 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 { Payment payment = null; try { payment = this.executePayment(paymentId,payerId,paypalPayConfig.getClientId(),paypalPayConfig.getClientSecret(),paypalPayConfig.getMode()); } catch (PayPalRESTException e) { log.error("支付回调失败",e); return paypalPayConfig.getCancelUrl(); } 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(); Currency transactionFee = sale.getTransactionFee(); String transationId = sale.getId(); String mOid = payment.getTransactions().get(0).getCustom(); OrderMasterDon masterDon = orderMasterDonService.getById(mOid); //更新订单支付状态 masterDon.setTransactionId(transationId); masterDon.setPayTime(new Date()); masterDon.setAbroadTransactionFee(new BigDecimal(transactionFee.getValue()).multiply(BigDecimal.valueOf(100))); masterDon.setStatus(OrderMasterDon.Status.hasPayment); log.info("支付成功订单:"+mOid+",支付交易号:"+transationId); log.info("支付成功订单:"+mOid+",返回参数:"+Jsons.toJson(payment)); //子订单 List orderDonList = orderDonService.list(Wrappers.lambdaQuery(OrderDon.class) .eq(OrderDon::getMOid, mOid)); orderDonList.forEach(orderDon -> { if (orderDon.getMoney().compareTo(BigDecimal.ZERO) > 0) { orderDon.setType(OrderDon.Type.PAYPAL); } orderDonService.orderNotify(orderDon); }); 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() + mOid); } log.info("支付失败返回参数:"+ Jsons.toJson(payment)); return paypalPayConfig.getCancelUrl(); } public Payment createPayment(Long mOid,String successUrl) throws PayPalRESTException { redisService.set(RedisService.key.ABROAD_SUCCESS_URL.getName() + mOid,successUrl,RedisService.key.ABROAD_SUCCESS_URL.getTimeout()); OrderMasterDon orderDon = orderMasterDonService.getById(mOid); BigDecimal money = orderDon.getMoney().divide(new BigDecimal(7),2,RoundingMode.HALF_DOWN); orderDon.setAbroadMoney(money); orderDon.setType(OrderMasterDon.Type.PAYPAL); orderMasterDonService.updateById(orderDon); if (OrderMasterDon.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; } }