// Used in single get APIs (getPayment / getPaymentByExternalKey)
  private Payment toPayment(
      final PaymentModelDao paymentModelDao,
      final boolean withPluginInfo,
      final Iterable<PluginProperty> properties,
      final TenantContext context,
      final InternalTenantContext tenantContext)
      throws PaymentApiException {
    final PaymentPluginApi plugin =
        getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), tenantContext);
    final List<PaymentTransactionInfoPlugin> pluginTransactions =
        withPluginInfo
            ? getPaymentTransactionInfoPlugins(plugin, paymentModelDao, properties, context)
            : null;

    return toPayment(paymentModelDao, pluginTransactions, tenantContext);
  }
Example #2
0
 public DefaultPayment(
     final PaymentModelDao src,
     @Nullable final PaymentInfoPlugin paymentPluginInfo,
     final List<PaymentAttemptModelDao> attempts,
     final List<RefundModelDao> refunds) {
   this(
       src.getId(),
       src.getCreatedDate(),
       src.getUpdatedDate(),
       src.getAccountId(),
       src.getInvoiceId(),
       src.getPaymentMethodId(),
       src.getAmount(),
       toPaidAmount(src.getPaymentStatus(), src.getAmount(), refunds),
       src.getCurrency(),
       src.getEffectiveDate(),
       src.getPaymentNumber(),
       src.getPaymentStatus(),
       paymentPluginInfo,
       toPaymentAttempts(attempts));
 }
  // Used in bulk get API (getAccountPayments)
  private Payment toPayment(
      final PaymentModelDao curPaymentModelDao,
      final Iterable<PaymentTransactionModelDao> curTransactionsModelDao,
      @Nullable final Iterable<PaymentTransactionInfoPlugin> pluginTransactions,
      final InternalTenantContext internalTenantContext) {
    final Ordering<PaymentTransaction> perPaymentTransactionOrdering =
        Ordering.<PaymentTransaction>from(
            new Comparator<PaymentTransaction>() {
              @Override
              public int compare(final PaymentTransaction o1, final PaymentTransaction o2) {
                return o1.getEffectiveDate().compareTo(o2.getEffectiveDate());
              }
            });

    // Need to filter for optimized codepaths looking up by account_record_id
    final Iterable<PaymentTransactionModelDao> filteredTransactions =
        Iterables.filter(
            curTransactionsModelDao,
            new Predicate<PaymentTransactionModelDao>() {
              @Override
              public boolean apply(final PaymentTransactionModelDao curPaymentTransactionModelDao) {
                return curPaymentTransactionModelDao
                    .getPaymentId()
                    .equals(curPaymentModelDao.getId());
              }
            });

    PaymentModelDao newPaymentModelDao = curPaymentModelDao;
    final Collection<PaymentTransaction> transactions = new LinkedList<PaymentTransaction>();
    for (final PaymentTransactionModelDao curPaymentTransactionModelDao : filteredTransactions) {
      PaymentTransactionModelDao newPaymentTransactionModelDao = curPaymentTransactionModelDao;

      final PaymentTransactionInfoPlugin paymentTransactionInfoPlugin =
          findPaymentTransactionInfoPlugin(newPaymentTransactionModelDao, pluginTransactions);
      if (paymentTransactionInfoPlugin != null) {
        // Make sure to invoke the Janitor task in case the plugin fixes its state on the fly
        // See https://github.com/killbill/killbill/issues/341
        final boolean hasChanged =
            incompletePaymentTransactionTask.updatePaymentAndTransactionIfNeededWithAccountLock(
                newPaymentModelDao,
                newPaymentTransactionModelDao,
                paymentTransactionInfoPlugin,
                internalTenantContext);
        if (hasChanged) {
          newPaymentModelDao =
              paymentDao.getPayment(newPaymentModelDao.getId(), internalTenantContext);
          newPaymentTransactionModelDao =
              paymentDao.getPaymentTransaction(
                  newPaymentTransactionModelDao.getId(), internalTenantContext);
        }
      }

      final PaymentTransaction transaction =
          new DefaultPaymentTransaction(
              newPaymentTransactionModelDao.getId(),
              newPaymentTransactionModelDao.getAttemptId(),
              newPaymentTransactionModelDao.getTransactionExternalKey(),
              newPaymentTransactionModelDao.getCreatedDate(),
              newPaymentTransactionModelDao.getUpdatedDate(),
              newPaymentTransactionModelDao.getPaymentId(),
              newPaymentTransactionModelDao.getTransactionType(),
              newPaymentTransactionModelDao.getEffectiveDate(),
              newPaymentTransactionModelDao.getTransactionStatus(),
              newPaymentTransactionModelDao.getAmount(),
              newPaymentTransactionModelDao.getCurrency(),
              newPaymentTransactionModelDao.getProcessedAmount(),
              newPaymentTransactionModelDao.getProcessedCurrency(),
              newPaymentTransactionModelDao.getGatewayErrorCode(),
              newPaymentTransactionModelDao.getGatewayErrorMsg(),
              paymentTransactionInfoPlugin);
      transactions.add(transaction);
    }

    final List<PaymentTransaction> sortedTransactions =
        perPaymentTransactionOrdering.immutableSortedCopy(transactions);
    return new DefaultPayment(
        curPaymentModelDao.getId(),
        curPaymentModelDao.getCreatedDate(),
        curPaymentModelDao.getUpdatedDate(),
        curPaymentModelDao.getAccountId(),
        curPaymentModelDao.getPaymentMethodId(),
        curPaymentModelDao.getPaymentNumber(),
        curPaymentModelDao.getExternalKey(),
        sortedTransactions);
  }
  @Override
  public void leavingState(final State state) throws OperationException {
    final DateTime utcNow = pluginControlPaymentAutomatonRunner.getClock().getUTCNow();

    // Retrieve the associated payment transaction, if any
    PaymentTransactionModelDao paymentTransactionModelDaoCandidate = null;
    if (stateContext.getTransactionId() != null) {
      paymentTransactionModelDaoCandidate =
          paymentDao.getPaymentTransaction(
              stateContext.getTransactionId(), stateContext.getInternalCallContext());
      Preconditions.checkNotNull(
          paymentTransactionModelDaoCandidate,
          "paymentTransaction cannot be null for id " + stateContext.getTransactionId());
    } else if (stateContext.getPaymentTransactionExternalKey() != null) {
      final List<PaymentTransactionModelDao> paymentTransactionModelDaos =
          paymentDao.getPaymentTransactionsByExternalKey(
              stateContext.getPaymentTransactionExternalKey(),
              stateContext.getInternalCallContext());
      if (!paymentTransactionModelDaos.isEmpty()) {
        paymentTransactionModelDaoCandidate =
            paymentTransactionModelDaos.get(paymentTransactionModelDaos.size() - 1);
      }
    }
    final PaymentTransactionModelDao paymentTransactionModelDao =
        paymentTransactionModelDaoCandidate != null
                && TRANSIENT_TRANSACTION_STATUSES.contains(
                    paymentTransactionModelDaoCandidate.getTransactionStatus())
            ? paymentTransactionModelDaoCandidate
            : null;

    if (stateContext.getPaymentId() != null && stateContext.getPaymentExternalKey() == null) {
      final PaymentModelDao payment =
          paymentDao.getPayment(stateContext.getPaymentId(), stateContext.getInternalCallContext());
      Preconditions.checkNotNull(
          payment, "payment cannot be null for id " + stateContext.getPaymentId());
      stateContext.setPaymentExternalKey(payment.getExternalKey());
      stateContext.setPaymentMethodId(payment.getPaymentMethodId());
    } else if (stateContext.getPaymentExternalKey() == null) {
      stateContext.setPaymentExternalKey(UUIDs.randomUUID().toString());
    }

    if (paymentTransactionModelDao != null) {
      stateContext.setPaymentTransactionExternalKey(
          paymentTransactionModelDao.getTransactionExternalKey());
    } else if (stateContext.getPaymentTransactionExternalKey() == null) {
      stateContext.setPaymentTransactionExternalKey(UUIDs.randomUUID().toString());
    }

    if (stateContext.getPaymentMethodId() == null) {
      // Similar logic in PaymentAutomatonRunner
      stateContext.setPaymentMethodId(stateContext.getAccount().getPaymentMethodId());
    }

    if (state.getName().equals(initialState.getName())
        || state.getName().equals(retriedState.getName())) {
      try {
        final PaymentAttemptModelDao attempt;
        if (paymentTransactionModelDao != null
            && paymentTransactionModelDao.getAttemptId() != null) {
          attempt =
              pluginControlPaymentAutomatonRunner
                  .getPaymentDao()
                  .getPaymentAttempt(
                      paymentTransactionModelDao.getAttemptId(),
                      stateContext.getInternalCallContext());
          Preconditions.checkNotNull(
              attempt,
              "attempt cannot be null for id " + paymentTransactionModelDao.getAttemptId());
        } else {
          //
          // We don't serialize any properties at this stage to avoid serializing sensitive
          // information.
          // However, if after going through the control plugins, the attempt end up in RETRIED
          // state,
          // the properties will be serialized in the enteringState callback (any plugin that sets a
          // retried date is responsible to correctly remove sensitive information such as CVV, ...)
          //
          final byte[] serializedProperties =
              PluginPropertySerializer.serialize(ImmutableList.<PluginProperty>of());

          attempt =
              new PaymentAttemptModelDao(
                  stateContext.getAccount().getId(),
                  stateContext.getPaymentMethodId(),
                  utcNow,
                  utcNow,
                  stateContext.getPaymentExternalKey(),
                  stateContext.getTransactionId(),
                  stateContext.getPaymentTransactionExternalKey(),
                  transactionType,
                  initialState.getName(),
                  stateContext.getAmount(),
                  stateContext.getCurrency(),
                  stateContext.getPaymentControlPluginNames(),
                  serializedProperties);
          pluginControlPaymentAutomatonRunner
              .getPaymentDao()
              .insertPaymentAttemptWithProperties(attempt, stateContext.getInternalCallContext());
        }

        stateContext.setAttemptId(attempt.getId());
      } catch (final PluginPropertySerializerException e) {
        throw new OperationException(e);
      }
    }
  }