use of org.killbill.billing.payment.dao.PaymentModelDao in project killbill by killbill.
the class PaymentAutomatonDAOHelper method createNewPaymentTransaction.
public void createNewPaymentTransaction() throws PaymentApiException {
final PaymentTransactionModelDao paymentTransactionModelDao;
final List<PaymentTransactionModelDao> existingTransactions;
if (paymentStateContext.getPaymentId() == null) {
final PaymentModelDao newPaymentModelDao = buildNewPaymentModelDao();
final PaymentTransactionModelDao newPaymentTransactionModelDao = buildNewPaymentTransactionModelDao(newPaymentModelDao.getId());
existingTransactions = ImmutableList.of();
final PaymentAndTransactionModelDao paymentAndTransactionModelDao = paymentDao.insertPaymentWithFirstTransaction(newPaymentModelDao, newPaymentTransactionModelDao, internalCallContext);
paymentTransactionModelDao = paymentAndTransactionModelDao.getPaymentTransactionModelDao();
} else {
existingTransactions = paymentDao.getTransactionsForPayment(paymentStateContext.getPaymentId(), internalCallContext);
if (existingTransactions.isEmpty()) {
throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_SUCCESS_PAYMENT, paymentStateContext.getPaymentId());
}
if (paymentStateContext.getCurrency() != null && existingTransactions.get(0).getCurrency() != paymentStateContext.getCurrency() && !TransactionType.CHARGEBACK.equals(paymentStateContext.getTransactionType())) {
// Note that we allow chargebacks in a different currency
throw new PaymentApiException(ErrorCode.PAYMENT_INVALID_PARAMETER, "currency", " should be " + existingTransactions.get(0).getCurrency() + " to match other existing transactions");
}
final PaymentTransactionModelDao newPaymentTransactionModelDao = buildNewPaymentTransactionModelDao(paymentStateContext.getPaymentId());
paymentTransactionModelDao = paymentDao.updatePaymentWithNewTransaction(paymentStateContext.getPaymentId(), newPaymentTransactionModelDao, internalCallContext);
}
// Update the context
paymentStateContext.setPaymentTransactionModelDao(paymentTransactionModelDao);
paymentStateContext.setOnLeavingStateExistingTransactions(existingTransactions);
}
use of org.killbill.billing.payment.dao.PaymentModelDao in project killbill by killbill.
the class PaymentAutomatonDAOHelper method buildNewPaymentModelDao.
private PaymentModelDao buildNewPaymentModelDao() {
final DateTime createdDate = utcNow;
final DateTime updatedDate = utcNow;
if (paymentStateContext.getPaymentIdForNewPayment() != null) {
return new PaymentModelDao(paymentStateContext.getPaymentIdForNewPayment(), createdDate, updatedDate, paymentStateContext.getAccount().getId(), paymentStateContext.getPaymentMethodId(), paymentStateContext.getPaymentExternalKey());
} else {
return new PaymentModelDao(createdDate, updatedDate, paymentStateContext.getAccount().getId(), paymentStateContext.getPaymentMethodId(), paymentStateContext.getPaymentExternalKey());
}
}
use of org.killbill.billing.payment.dao.PaymentModelDao in project killbill by killbill.
the class PaymentProcessor method performOperation.
private Payment performOperation(final boolean isApiPayment, final boolean runJanitor, @Nullable final UUID attemptId, final TransactionType transactionType, final Account account, @Nullable final UUID paymentMethodId, @Nullable final UUID paymentId, @Nullable final UUID transactionId, @Nullable final BigDecimal amount, @Nullable final Currency currency, @Nullable final DateTime effectiveDate, @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey, @Nullable final UUID paymentIdForNewPayment, @Nullable final UUID paymentTransactionIdForNewPaymentTransaction, final boolean shouldLockAccountAndDispatch, @Nullable final OperationResult overridePluginOperationResult, final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
final PaymentStateContext paymentStateContext = paymentAutomatonRunner.buildPaymentStateContext(isApiPayment, transactionType, account, attemptId, paymentMethodId != null ? paymentMethodId : account.getPaymentMethodId(), paymentId, transactionId, paymentExternalKey, paymentTransactionExternalKey, amount, currency, effectiveDate, paymentIdForNewPayment, paymentTransactionIdForNewPaymentTransaction, shouldLockAccountAndDispatch, overridePluginOperationResult, properties, callContext, internalCallContext);
final PaymentAutomatonDAOHelper daoHelper = paymentAutomatonRunner.buildDaoHelper(paymentStateContext, internalCallContext);
String currentStateName = null;
if (paymentStateContext.getPaymentId() != null) {
PaymentModelDao paymentModelDao = daoHelper.getPayment();
// Sanity: verify the payment belongs to the right account (in case it was looked-up by payment or transaction external key)
if (!paymentModelDao.getAccountRecordId().equals(internalCallContext.getAccountRecordId())) {
throw new PaymentApiException(ErrorCode.PAYMENT_DIFFERENT_ACCOUNT_ID, paymentStateContext.getPaymentId());
}
// Note: the list needs to be modifiable for invokeJanitor
final Collection<PaymentTransactionModelDao> paymentTransactionsForCurrentPayment = new LinkedList<PaymentTransactionModelDao>(daoHelper.getPaymentDao().getTransactionsForPayment(paymentStateContext.getPaymentId(), paymentStateContext.getInternalCallContext()));
// prevent disallowed transitions in case the state couldn't be fixed (or if it's already in a final state).
if (runJanitor) {
final PaymentPluginApi plugin = getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), true, internalCallContext);
final List<PaymentTransactionInfoPlugin> pluginTransactions = paymentRefresher.getPaymentTransactionInfoPlugins(plugin, paymentModelDao, properties, callContext);
paymentModelDao = paymentRefresher.invokeJanitor(paymentModelDao, paymentTransactionsForCurrentPayment, pluginTransactions, isApiPayment, internalCallContext);
}
if (paymentStateContext.getPaymentTransactionExternalKey() != null) {
final List<PaymentTransactionModelDao> allPaymentTransactionsForKey = daoHelper.getPaymentDao().getPaymentTransactionsByExternalKey(paymentStateContext.getPaymentTransactionExternalKey(), internalCallContext);
runSanityOnTransactionExternalKey(allPaymentTransactionsForKey, paymentStateContext, internalCallContext);
}
if (paymentStateContext.getTransactionId() != null || paymentStateContext.getPaymentTransactionExternalKey() != null) {
// If a transaction id or key is passed, we are maybe completing an existing transaction (unless a new key was provided)
PaymentTransactionModelDao transactionToComplete = findTransactionToCompleteAndRunSanityChecks(paymentModelDao, paymentTransactionsForCurrentPayment, paymentStateContext, internalCallContext);
if (transactionToComplete != null) {
final UUID transactionToCompleteId = transactionToComplete.getId();
transactionToComplete = Iterables.<PaymentTransactionModelDao>find(paymentTransactionsForCurrentPayment, new Predicate<PaymentTransactionModelDao>() {
@Override
public boolean apply(final PaymentTransactionModelDao input) {
return transactionToCompleteId.equals(input.getId());
}
});
// We can't tell where we should be in the state machine - bail (cannot be enforced by the state machine unfortunately because UNKNOWN and PLUGIN_FAILURE are both treated as EXCEPTION)
if (transactionToComplete.getTransactionStatus() == TransactionStatus.UNKNOWN) {
throw new PaymentApiException(ErrorCode.PAYMENT_INVALID_OPERATION, paymentStateContext.getTransactionType(), transactionToComplete.getTransactionStatus());
}
paymentStateContext.setPaymentTransactionModelDao(transactionToComplete);
}
}
// Use the original payment method id of the payment being completed
paymentStateContext.setPaymentMethodId(paymentModelDao.getPaymentMethodId());
// We always take the last successful state name to permit retries on failures
currentStateName = paymentModelDao.getLastSuccessStateName();
}
paymentAutomatonRunner.run(paymentStateContext, daoHelper, currentStateName, transactionType);
return paymentRefresher.getPayment(paymentStateContext.getPaymentModelDao(), true, false, isApiPayment, properties, callContext, internalCallContext);
}
use of org.killbill.billing.payment.dao.PaymentModelDao in project killbill by killbill.
the class DefaultControlInitiated method leavingState.
@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) {
final UUID paymentIdForNewPayment = UUIDs.randomUUID();
stateContext.setPaymentIdForNewPayment(paymentIdForNewPayment);
stateContext.setPaymentExternalKey(paymentIdForNewPayment.toString());
}
if (paymentTransactionModelDao != null) {
stateContext.setPaymentTransactionModelDao(paymentTransactionModelDao);
stateContext.setProcessedAmount(paymentTransactionModelDao.getProcessedAmount());
stateContext.setProcessedCurrency(paymentTransactionModelDao.getProcessedCurrency());
} else if (stateContext.getPaymentTransactionExternalKey() == null) {
final UUID paymentTransactionIdForNewPaymentTransaction = UUIDs.randomUUID();
stateContext.setPaymentTransactionIdForNewPaymentTransaction(paymentTransactionIdForNewPaymentTransaction);
stateContext.setPaymentTransactionExternalKey(paymentTransactionIdForNewPaymentTransaction.toString());
}
// In this case, we also want to provide the associated plugin name
if (stateContext.getPaymentMethodId() != null) {
final PaymentMethodModelDao pm = paymentDao.getPaymentMethod(stateContext.getPaymentMethodId(), stateContext.getInternalCallContext());
// Payment method was deleted
if (pm != null) {
stateContext.setOriginalPaymentPluginName(pm.getPluginName());
}
}
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);
}
}
}
use of org.killbill.billing.payment.dao.PaymentModelDao in project killbill by killbill.
the class InvoicePaymentControlPluginApi method getPurchasedTransactions.
private List<PaymentTransactionModelDao> getPurchasedTransactions(final String paymentExternalKey, final InternalCallContext internalContext) {
final PaymentModelDao payment = paymentDao.getPaymentByExternalKey(paymentExternalKey, internalContext);
if (payment == null) {
return Collections.emptyList();
}
final List<PaymentTransactionModelDao> transactions = paymentDao.getTransactionsForPayment(payment.getId(), internalContext);
if (transactions == null || transactions.size() == 0) {
return Collections.emptyList();
}
return ImmutableList.copyOf(Iterables.filter(transactions, new Predicate<PaymentTransactionModelDao>() {
@Override
public boolean apply(final PaymentTransactionModelDao input) {
return input.getTransactionType() == TransactionType.PURCHASE;
}
}));
}
Aggregations