use of org.killbill.billing.payment.plugin.api.PaymentPluginApiException in project killbill by killbill.
the class TestPaymentApi method testCreatePurchasePaymentPluginException.
@Test(groups = "slow")
public void testCreatePurchasePaymentPluginException() {
mockPaymentProviderPlugin.makeNextPaymentFailWithException();
final BigDecimal requestedAmount = BigDecimal.TEN;
final String paymentExternalKey = "pay external key";
final String transactionExternalKey = "txn external key";
try {
paymentApi.createPurchase(account, account.getPaymentMethodId(), null, requestedAmount, Currency.AED, null, paymentExternalKey, transactionExternalKey, ImmutableList.<PluginProperty>of(), callContext);
fail();
} catch (PaymentApiException e) {
assertTrue(e.getCause() instanceof PaymentPluginApiException);
}
}
use of org.killbill.billing.payment.plugin.api.PaymentPluginApiException in project killbill by killbill.
the class MockPaymentProviderPlugin method getPaymentTransactionInfoPluginResult.
private PaymentTransactionInfoPlugin getPaymentTransactionInfoPluginResult(final UUID kbPaymentId, final UUID kbTransactionId, final TransactionType type, @Nullable final BigDecimal amount, @Nullable final Currency currency, final Iterable<PluginProperty> pluginProperties) throws PaymentPluginApiException {
if (makePluginWaitSomeMilliseconds.get() > 0) {
try {
Thread.sleep(makePluginWaitSomeMilliseconds.get());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new PaymentPluginApiException("An Interruption occurred while the Thread was sleeping.", e);
}
}
if (makeNextPaymentFailWithException.getAndSet(false)) {
throw new PaymentPluginApiException("", "test error");
}
final PluginProperty paymentPluginStatusOverride = Iterables.tryFind(pluginProperties, new Predicate<PluginProperty>() {
@Override
public boolean apply(final PluginProperty input) {
return PLUGIN_PROPERTY_PAYMENT_PLUGIN_STATUS_OVERRIDE.equals(input.getKey());
}
}).orNull();
final PaymentPluginStatus status;
if (paymentPluginStatusOverride != null && paymentPluginStatusOverride.getValue() != null) {
status = PaymentPluginStatus.valueOf(paymentPluginStatusOverride.getValue().toString());
} else if (makeAllPaymentsFailWithError.get() || makeNextPaymentFailWithError.getAndSet(false)) {
status = PaymentPluginStatus.ERROR;
} else if (makeNextPaymentFailWithCancellation.getAndSet(false)) {
status = PaymentPluginStatus.CANCELED;
} else if (makeNextPaymentPending.getAndSet(false)) {
status = PaymentPluginStatus.PENDING;
} else if (makeNextPaymentUnknown.getAndSet(false)) {
status = PaymentPluginStatus.UNDEFINED;
} else {
status = PaymentPluginStatus.PROCESSED;
}
final String errorCode = status == PaymentPluginStatus.PROCESSED ? "" : GATEWAY_ERROR_CODE;
final String error = status == PaymentPluginStatus.PROCESSED ? "" : GATEWAY_ERROR;
InternalPaymentInfo info = payments.get(kbPaymentId.toString());
if (info == null) {
info = new InternalPaymentInfo();
payments.put(kbPaymentId.toString(), info);
}
final BigDecimal overrideNextProcessedAmount = this.overrideNextProcessedAmount.getAndSet(null);
final BigDecimal processedAmount = overrideNextProcessedAmount != null ? overrideNextProcessedAmount : amount;
Currency processedCurrency = overrideNextProcessedCurrency.getAndSet(null);
if (processedCurrency == null) {
processedCurrency = currency;
}
final PaymentTransactionInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, type, processedAmount, processedCurrency, clock.getUTCNow(), clock.getUTCNow(), status, errorCode, error, null, null, ImmutableList.<PluginProperty>copyOf(pluginProperties));
List<PaymentTransactionInfoPlugin> existingTransactions = paymentTransactions.get(kbPaymentId.toString());
if (existingTransactions == null) {
existingTransactions = new ArrayList<PaymentTransactionInfoPlugin>();
paymentTransactions.put(kbPaymentId.toString(), existingTransactions);
}
final Iterator<PaymentTransactionInfoPlugin> iterator = existingTransactions.iterator();
while (iterator.hasNext()) {
final PaymentTransactionInfoPlugin existingTransaction = iterator.next();
if (existingTransaction.getKbTransactionPaymentId().equals(kbTransactionId)) {
info.addAmount(type, existingTransaction.getAmount().negate());
iterator.remove();
}
}
existingTransactions.add(result);
info.addAmount(type, result.getAmount());
return result;
}
use of org.killbill.billing.payment.plugin.api.PaymentPluginApiException in project killbill by killbill.
the class PaymentMethodProcessor method refreshPaymentMethods.
/**
* This refreshed the payment methods from the plugin for cases when adding payment method does not flow through KB because of PCI compliance
* issues. The logic below is not optimal because there is no atomicity in the step but the good news is that this is idempotent so can always be
* replayed if necessary-- partial failure scenario.
*
* @param pluginName
* @param account
* @param context
* @return the list of payment methods -- should be identical between KB, the plugin view-- if it keeps a state-- and the gateway.
* @throws PaymentApiException
*/
public List<PaymentMethod> refreshPaymentMethods(final String pluginName, final Account account, final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext context) throws PaymentApiException {
// Don't hold the account lock while fetching the payment methods from the gateway as those could change anyway
final PaymentPluginApi pluginApi = getPaymentPluginApi(pluginName);
final List<PaymentMethodInfoPlugin> pluginPms;
try {
pluginPms = pluginApi.getPaymentMethods(account.getId(), true, properties, callContext);
// The method should never return null by convention, but let's not trust the plugin...
if (pluginPms == null) {
log.debug("No payment methods defined on the account {} for plugin {}", account.getId(), pluginName);
return ImmutableList.<PaymentMethod>of();
}
} catch (final PaymentPluginApiException e) {
throw new PaymentApiException(e, ErrorCode.PAYMENT_REFRESH_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
}
try {
final PluginDispatcherReturnType<List<PaymentMethod>> result = new WithAccountLock<List<PaymentMethod>, PaymentApiException>(paymentConfig).processAccountWithLock(locker, account.getId(), new DispatcherCallback<PluginDispatcherReturnType<List<PaymentMethod>>, PaymentApiException>() {
@Override
public PluginDispatcherReturnType<List<PaymentMethod>> doOperation() throws PaymentApiException {
UUID defaultPaymentMethodId = null;
final List<PaymentMethodInfoPlugin> pluginPmsWithId = new ArrayList<PaymentMethodInfoPlugin>();
final List<PaymentMethodModelDao> finalPaymentMethods = new ArrayList<PaymentMethodModelDao>();
for (final PaymentMethodInfoPlugin cur : pluginPms) {
// If the kbPaymentId is NULL, the plugin does not know about it, so we create a new UUID
final UUID paymentMethodId = cur.getPaymentMethodId() != null ? cur.getPaymentMethodId() : UUIDs.randomUUID();
final String externalKey = cur.getExternalPaymentMethodId() != null ? cur.getExternalPaymentMethodId() : paymentMethodId.toString();
final PaymentMethod input = new DefaultPaymentMethod(paymentMethodId, externalKey, account.getId(), pluginName);
final PaymentMethodModelDao pmModel = new PaymentMethodModelDao(input.getId(), input.getExternalKey(), input.getCreatedDate(), input.getUpdatedDate(), input.getAccountId(), input.getPluginName(), input.isActive());
finalPaymentMethods.add(pmModel);
pluginPmsWithId.add(new DefaultPaymentMethodInfoPlugin(cur, paymentMethodId));
// will always return false - it's Kill Bill in that case which is responsible to manage default payment methods
if (cur.isDefault()) {
defaultPaymentMethodId = paymentMethodId;
}
}
final List<PaymentMethodModelDao> refreshedPaymentMethods = paymentDao.refreshPaymentMethods(pluginName, finalPaymentMethods, context);
try {
pluginApi.resetPaymentMethods(account.getId(), pluginPmsWithId, properties, callContext);
} catch (final PaymentPluginApiException e) {
throw new PaymentApiException(e, ErrorCode.PAYMENT_REFRESH_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
}
try {
updateDefaultPaymentMethodIfNeeded(pluginName, account, defaultPaymentMethodId, context);
} catch (final AccountApiException e) {
throw new PaymentApiException(e);
}
final List<PaymentMethod> result = ImmutableList.<PaymentMethod>copyOf(Collections2.transform(refreshedPaymentMethods, new Function<PaymentMethodModelDao, PaymentMethod>() {
@Override
public PaymentMethod apply(final PaymentMethodModelDao input) {
return new DefaultPaymentMethod(input, null);
}
}));
return PluginDispatcher.createPluginDispatcherReturnType(result);
}
});
return result.getReturnType();
} catch (final Exception e) {
throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, MoreObjects.firstNonNull(e.getMessage(), ""));
}
}
use of org.killbill.billing.payment.plugin.api.PaymentPluginApiException in project killbill by killbill.
the class PaymentMethodProcessor method buildDefaultPaymentMethod.
private PaymentMethod buildDefaultPaymentMethod(final PaymentMethodModelDao paymentMethodModelDao, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext context) throws PaymentApiException {
final PaymentMethodPlugin paymentMethodPlugin;
if (withPluginInfo) {
try {
final PaymentPluginApi pluginApi = getPaymentPluginApi(paymentMethodModelDao.getPluginName());
paymentMethodPlugin = pluginApi.getPaymentMethodDetail(paymentMethodModelDao.getAccountId(), paymentMethodModelDao.getId(), properties, tenantContext);
} catch (final PaymentPluginApiException e) {
throw new PaymentApiException(e, ErrorCode.PAYMENT_GET_PAYMENT_METHODS, paymentMethodModelDao.getAccountId(), paymentMethodModelDao.getId());
}
} else {
paymentMethodPlugin = null;
}
return new DefaultPaymentMethod(paymentMethodModelDao, paymentMethodPlugin);
}
use of org.killbill.billing.payment.plugin.api.PaymentPluginApiException in project killbill by killbill.
the class PaymentRefresher method searchPayments.
public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final boolean withAttempts, final boolean isApiPayment, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
final PaymentPluginApi pluginApi = getPaymentPluginApi(pluginName);
final Pagination<PaymentTransactionInfoPlugin> paymentTransactionInfoPlugins;
try {
paymentTransactionInfoPlugins = pluginApi.searchPayments(searchKey, offset, limit, properties, tenantContext);
} catch (final PaymentPluginApiException e) {
throw new PaymentApiException(e, ErrorCode.PAYMENT_PLUGIN_SEARCH_PAYMENTS, pluginName, searchKey);
}
// Cannot easily stream here unfortunately, since we need to merge PaymentTransactionInfoPlugin into Payment (no order assumed)
final Multimap<UUID, PaymentTransactionInfoPlugin> payments = HashMultimap.<UUID, PaymentTransactionInfoPlugin>create();
for (final PaymentTransactionInfoPlugin paymentTransactionInfoPlugin : paymentTransactionInfoPlugins) {
if (paymentTransactionInfoPlugin.getKbPaymentId() == null) {
// Garbage from the plugin?
log.debug("Plugin {} returned a payment without a kbPaymentId for searchKey {}", pluginName, searchKey);
} else {
payments.put(paymentTransactionInfoPlugin.getKbPaymentId(), paymentTransactionInfoPlugin);
}
}
final Collection<Payment> results = new LinkedList<Payment>();
for (final UUID paymentId : payments.keys()) {
final Payment result = toPayment(paymentId, withPluginInfo ? payments.get(paymentId) : ImmutableList.<PaymentTransactionInfoPlugin>of(), withAttempts, isApiPayment, internalTenantContext);
if (result != null) {
results.add(result);
}
}
return new DefaultPagination<Payment>(paymentTransactionInfoPlugins, limit, results.iterator());
}
Aggregations