gt:             PaymentSettings settings = getPaymentSettings();
context: /*
   * (C) Copyright ${year} Nuxeo SA (http://nuxeo.com/) and others.
   *
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the GNU Lesser General Public License
   * (LGPL) version 2.1 which accompanies this distribution, and is available at
   * http://www.gnu.org/licenses/lgpl-2.1.html
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   * Lesser General Public License for more details.
   */
  
  package com.goodhuddle.huddle.service.impl;
  
  import com.goodhuddle.huddle.domain.*;
  import com.goodhuddle.huddle.domain.Subscription;
  import com.goodhuddle.huddle.repository.*;
  import com.goodhuddle.huddle.service.HuddleService;
  import com.goodhuddle.huddle.service.MemberService;
  import com.goodhuddle.huddle.service.PaymentService;
  import com.goodhuddle.huddle.service.exception.*;
  import com.goodhuddle.huddle.service.request.payment.CreateSubscriptionRequest;
  import com.goodhuddle.huddle.service.request.payment.ProcessPaymentRequest;
  import com.goodhuddle.huddle.service.request.payment.SearchPaymentsRequest;
  import com.goodhuddle.huddle.service.request.payment.SearchSubscriptionsRequest;
  import com.goodhuddle.huddle.service.request.payment.settings.UpdatePaymentSettingsRequest;
  import com.stripe.exception.*;
  import com.stripe.model.*;
  import org.joda.time.DateTime;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.data.domain.Page;
  import org.springframework.data.domain.PageRequest;
  import org.springframework.data.domain.Sort;
  import org.springframework.stereotype.Service;
  import org.springframework.transaction.annotation.Transactional;
  
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  @Service
  public class PaymentServiceImpl implements PaymentService {
  
      private static final Logger log = LoggerFactory.getLogger(PaymentServiceImpl.class);
  
      public static final String META_HUDDLE = "gh-huddle";
  
      private final PaymentRepository paymentRepository;
      private final SubscriptionRepository subscriptionRepository;
      private final PaymentSettingsRepository paymentSettingsRepository;
      private final MemberService memberService;
      private final HuddleService huddleService;
  
      @Autowired
      public PaymentServiceImpl(PaymentRepository paymentRepository,
                                SubscriptionRepository subscriptionRepository,
                                PaymentSettingsRepository paymentSettingsRepository,
                                MemberService memberService,
                                HuddleService huddleService) {
  
          this.paymentRepository = paymentRepository;
          this.subscriptionRepository = subscriptionRepository;
          this.paymentSettingsRepository = paymentSettingsRepository;
          this.memberService = memberService;
          this.huddleService = huddleService;
      }
  
      @Override
      public PaymentSettings getPaymentSettings() {
          List<PaymentSettings> settings = paymentSettingsRepository.findByHuddle(huddleService.getHuddle());
          return settings.size() > 0 ? settings.get(0) : new PaymentSettings(huddleService.getHuddle());
      }
  
      @Override
      public String getHuddlePublishableKey() {
          PaymentSettings settings = getPaymentSettings();
          return settings.getPublishableKey();
      }
  
      @Override
      public void checkChangeSettingsAllowed() throws SubscriptionsExistException {
          if (subscriptionRepository.countByHuddleAndStatus(huddleService.getHuddle(), SubscriptionStatus.active) > 0) {
              throw new SubscriptionsExistException(
                      "You must cancel all active subscriptions before changing your payment settings");
          }
      }
  
      @Override
      @Transactional(readOnly = false)
      public PaymentSettings updatePaymentSettings(UpdatePaymentSettingsRequest request) throws SubscriptionsExistException {
          checkChangeSettingsAllowed();
          PaymentSettings settings = getPaymentSettings();
          settings.setSecretKey(request.getSecretKey());
          settings.setPublishableKey(request.getPublishableKey());
          paymentSettingsRepository.save(settings);
          return settings;
      }
  
      @Override
      public Payment getPayment(long id) {
          return paymentRepository.findByHuddleAndId(huddleService.getHuddle(), id);
      }
  
      @Override
      public Page<? extends Payment> searchPayments(SearchPaymentsRequest request) {
          return paymentRepository.findAll(PaymentSpecification.search(huddleService.getHuddle(), request),
                  new PageRequest(request.getPage(), request.getSize(), Sort.Direction.DESC, "paidOn"));
      }
  
  
      @Override
      @Transactional(readOnly = false)
      public Payment processPayment(ProcessPaymentRequest request) throws PaymentFailedException, PaymentsNotSetupException {
  
          try {
  
              Member payee = memberService.getOrCreateMemberByEmail(request.getEmail());
              PaymentSettings settings = getPaymentSettings();
  
              log.info("Making one-off Stripe payment of ${} by ", request.getAmountInCents() / 100, request.getEmail());
              final Map<String, Object> chargeParams = new HashMap<>();
              chargeParams.put("amount", request.getAmountInCents());
              chargeParams.put("currency", request.getCurrency());
              chargeParams.put("card", request.getToken());
  
              Map<String, String> metaData = new HashMap<>();
              metaData.put("huddle", huddleService.getHuddle().getSlug());
              chargeParams.put("metadata", metaData);
  
              final Charge charge = Charge.create(chargeParams, settings.getSecretKey());
              log.debug("Stripe payment completed with success=", charge.getPaid());
  
              if (charge.getPaid()) {
  
                  DateTime paymentDate = new DateTime(charge.getCreated() * 1000);
                  Payment payment = paymentRepository.save(new Payment(
                          huddleService.getHuddle(),
                          request.getType(),
                          charge.getId(),
                          charge.getBalanceTransaction(),
                          request.getAmountInCents(),
                          charge.getCurrency(),
                          request.getDescription(),
                          request.getEmail(),
                          payee,
                          paymentDate
                  ));
  
                  payment = postProcessPayment(payment);
  
                  log.info("New payment '{}' processed with ID '{}'", request.getDescription(), payment.getId());
                  return payment;
              } else {
                  log.warn("Payment failed: {} ({})", charge.getFailureMessage(), charge.getFailureCode());
                  throw new PaymentFailedException("Payment failed: " + charge.getFailureMessage(), null);
              }
  
  
          } catch (AuthenticationException | APIConnectionException | CardException | InvalidRequestException | APIException e) {
              log.error("Payment failed", e);
              throw new PaymentFailedException("Payment failed: " + e, null);
          }
      }
  
      @Override
      public Subscription getSubscription(long id) {
          return subscriptionRepository.findByHuddleAndId(huddleService.getHuddle(), id);
      }
  
      @Override
      public Subscription getSubscriptionByStripeCustomerId(String stripeCustomerId) {
          return subscriptionRepository.findByStripeCustomerId(stripeCustomerId);
      }
  
      @Override
      public Page<? extends Subscription> searchSubscriptions(SearchSubscriptionsRequest request) {
          return subscriptionRepository.findAll(SubscriptionSpecification.search(huddleService.getHuddle(), request),
                  new PageRequest(request.getPage(), request.getSize(), Sort.Direction.DESC, "createdOn"));
      }
  
      @Override
      @Transactional(readOnly = false)
      public Subscription createSubscription(CreateSubscriptionRequest request)
              throws PaymentFailedException, PaymentsNotSetupException {
  
          try {
  
              Member payee = memberService.getOrCreateMemberByEmail(request.getEmail());
              PaymentSettings settings = getPaymentSettings();
  
              log.info("Making recurring Stripe payment of ${} by {}", request.getAmountInCents() / 100, request.getEmail());
  
              String planId = createPaymentPlanId(request);
              Plan plan;
              try {
                  plan = Plan.retrieve(planId, settings.getSecretKey());
              } catch (InvalidRequestException e) {
                  // plan does not exist, create plan
                  Map<String, Object> planParams = new HashMap<>();
                  planParams.put("id", planId);
                  planParams.put("name", createPaymentPlanName(request));
                  planParams.put("amount", request.getAmountInCents());
                  planParams.put("currency", request.getCurrency());
                  planParams.put("interval", request.getFrequency().name());
  
                  Map<String, String> metaData = new HashMap<>();
                  metaData.put(META_HUDDLE, huddleService.getHuddle().getSlug());
                  planParams.put("metadata", metaData);
  
                  plan = Plan.create(planParams, settings.getSecretKey());
                  log.debug("New Stripe payment plan created with ID '{}'", plan.getId());
              }
  
              // create customer / subscription
  
              final Map<String, Object> customerParams = new HashMap<>();
              customerParams.put("email", request.getEmail());
              customerParams.put("plan", plan.getId());
              customerParams.put("card", request.getToken());
  
              Map<String, String> metaData = new HashMap<>();
              metaData.put(META_HUDDLE, huddleService.getHuddle().getSlug());
              customerParams.put("metadata", metaData);
  
              final Customer customer = Customer.create(customerParams, settings.getSecretKey());
  
              String stripeSubscriptionId = customer.getSubscriptions().getData().get(0).getId();
  
              Subscription subscription = subscriptionRepository.save(new Subscription(huddleService.getHuddle(),
                      request.getFrequency(), request.getType(), customer.getId(), stripeSubscriptionId, 
                      request.getAmountInCents(), request.getCurrency(), request.getDescription(), payee));
  
              subscriptionRepository.save(subscription);
              log.debug("Stripe recurring payment setup with subscription ID {}", stripeSubscriptionId);
  
              return subscription;
  
          } catch (AuthenticationException | APIConnectionException | CardException | InvalidRequestException | APIException e) {
              log.error("Payment failed", e);
              throw new PaymentFailedException("Payment failed: " + e, null);
          }
      }
  
      @Override
      @Transactional(readOnly = false)
      public Subscription cancelSubscription(long id) throws SubscriptionErrorException {
          try {
  
              Subscription subscription = subscriptionRepository.findByHuddleAndId(huddleService.getHuddle(), id);
  