public T dispatchWithAccountLockAndTimeout(final Callable<T> task, final long timeout, final TimeUnit unit) throws PaymentApiException, TimeoutException { try { final Future<T> future = executor.submit(task); return future.get(timeout, unit); } catch (ExecutionException e) { if (e.getCause() instanceof PaymentApiException) { throw (PaymentApiException) e.getCause(); } else { throw new PaymentApiException(ErrorCode.PAYMENT_INTERNAL_ERROR, e.getMessage()); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new PaymentApiException(ErrorCode.PAYMENT_INTERNAL_ERROR, e.getMessage()); } }
@Subscribe public void processInvoiceEvent(final InvoiceCreationInternalEvent event) { log.info("Received invoice creation notification for account {} and invoice {}", event.getAccountId(), event.getInvoiceId()); final Account account; try { final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "PaymentRequestProcessor", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken()); account = accountApi.getAccountById(event.getAccountId(), internalContext); paymentProcessor.createPayment(account, event.getInvoiceId(), null, internalContext, false, false); } catch (AccountApiException e) { log.error("Failed to process invoice payment", e); } catch (PaymentApiException e) { // Log as error unless: if (e.getCode() != ErrorCode.PAYMENT_NULL_INVOICE.getCode() /* Nothing left to be paid */ && e.getCode() != ErrorCode.PAYMENT_CREATE_PAYMENT.getCode() /* User payment error */) { log.error("Failed to process invoice payment {}", e.toString()); } } } }
fail("Failed to create payment", e); } else { log.info(e.getMessage()); assertEquals(e.getCode(), ErrorCode.PAYMENT_AMOUNT_DENIED.getCode());
@Override public Response toResponse(final PaymentApiException exception) { if (exception.getCode() == ErrorCode.PAYMENT_ADD_PAYMENT_METHOD.getCode()) { return buildBadRequestResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.PAYMENT_AMOUNT_DENIED.getCode()) { return buildBadRequestResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.PAYMENT_BAD_ACCOUNT.getCode()) { return buildInternalErrorResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.PAYMENT_CREATE_PAYMENT.getCode()) { return buildInternalErrorResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT.getCode()) { return buildInternalErrorResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT_BAD.getCode()) { return buildInternalErrorResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.PAYMENT_CREATE_PAYMENT_FOR_ATTEMPT_WITH_NON_POSITIVE_INV.getCode()) { return buildInternalErrorResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.PAYMENT_CREATE_PAYMENT_PROVIDER_ACCOUNT.getCode()) { return buildInternalErrorResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.PAYMENT_CREATE_REFUND.getCode()) { return buildInternalErrorResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.PAYMENT_DEL_DEFAULT_PAYMENT_METHOD.getCode()) { return buildBadRequestResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.PAYMENT_DEL_PAYMENT_METHOD.getCode()) { return buildInternalErrorResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.PAYMENT_GET_PAYMENT_METHODS.getCode()) { return buildInternalErrorResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.PAYMENT_GET_PAYMENT_PROVIDER.getCode()) { return buildInternalErrorResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.PAYMENT_GET_PAYMENT_PROVIDER_ACCOUNT.getCode()) { return buildInternalErrorResponse(exception, uriInfo);
@Override public void rebuildAnalyticsForAccount(final Account account, final CallContext context) { final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), context); // Update the BAC row bacDao.accountUpdated(account.getId(), internalCallContext); // Update BST for all bundles final Set<UUID> bundleIds = updateBST(account, internalCallContext); // Update BIN and BII for all invoices invoiceDao.rebuildInvoicesForAccount(account.getId(), internalCallContext); // Update BIP for all invoices try { updateBIP(account, internalCallContext); } catch (PaymentApiException e) { // Log and ignore log.warn(e.toString()); } // Update BOS for all bundles (only blockable supported today) // TODO: support other blockables for (final UUID bundleId : bundleIds) { bosDao.overdueStatusChanged(Type.SUBSCRIPTION_BUNDLE, bundleId, internalCallContext); } // Update bac_tags // TODO: refresh all tags updateTags(account, internalCallContext); }
paymentApi.createPayment(account, invoice.getId(), requestedAmount, callContext); } catch (PaymentApiException e) { Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_NO_DEFAULT_PAYMENT_METHOD.getCode());
@Override public Void call() throws Exception { throw new PaymentApiException(ErrorCode.PAYMENT_ADD_PAYMENT_METHOD, "foo", "foo"); } }, 100, TimeUnit.MILLISECONDS);
@Override public Payment getPayment(final UUID paymentId, final InternalTenantContext context) throws PaymentApiException { final Payment payment = paymentProcessor.getPayment(paymentId, false, context); if (payment == null) { throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT, paymentId); } return payment; }
protected PaymentPluginApi getPaymentPluginApi(final String pluginName) throws PaymentApiException { final PaymentPluginApi pluginApi = pluginRegistry.getServiceForName(pluginName); if (pluginApi == null) { throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_PLUGIN, pluginName); } return pluginApi; }
protected void setAccountAutoPayOff(final UUID accountId, final InternalCallContext context) throws PaymentApiException { try { tagInternalApi.addTag(accountId, ObjectType.ACCOUNT, ControlTagType.AUTO_PAY_OFF.getId(), context); } catch (TagApiException e) { log.error("Failed to add AUTO_PAY_OFF on account " + accountId, e); throw new PaymentApiException(ErrorCode.PAYMENT_INTERNAL_ERROR, "Failed to add AUTO_PAY_OFF on account " + accountId); } }
@Override public Pagination<PaymentInfoPlugin> build() throws PaymentApiException { try { return pluginApi.searchPayments(searchKey, offset, limit, buildTenantContext(internalTenantContext)); } catch (final PaymentPluginApiException e) { throw new PaymentApiException(e, ErrorCode.PAYMENT_PLUGIN_SEARCH_PAYMENTS, pluginName, searchKey); } } },
@Override public Refund createRefund(final Account account, final UUID paymentId, final BigDecimal refundAmount, final CallContext context) throws PaymentApiException { if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) { throw new PaymentApiException(ErrorCode.PAYMENT_REFUND_AMOUNT_NEGATIVE_OR_NULL); } return refundProcessor.createRefund(account, paymentId, refundAmount, false, ImmutableMap.<UUID, BigDecimal>of(), internalCallContextFactory.createInternalCallContext(account.getId(), context)); }
@Override public Pagination<PaymentMethodPlugin> build() throws PaymentApiException { try { return pluginApi.searchPaymentMethods(searchKey, offset, limit, buildTenantContext(internalTenantContext)); } catch (final PaymentPluginApiException e) { throw new PaymentApiException(e, ErrorCode.PAYMENT_PLUGIN_SEARCH_PAYMENT_METHODS, pluginName, searchKey); } } },
@Override public Refund createRefundWithAdjustment(final Account account, final UUID paymentId, final BigDecimal refundAmount, final CallContext context) throws PaymentApiException { if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) { throw new PaymentApiException(ErrorCode.PAYMENT_REFUND_AMOUNT_NEGATIVE_OR_NULL); } return refundProcessor.createRefund(account, paymentId, refundAmount, true, ImmutableMap.<UUID, BigDecimal>of(), internalCallContextFactory.createInternalCallContext(account.getId(), context)); }
private BigDecimal getAndValidatePaymentAmount(final Invoice invoice, @Nullable final BigDecimal inputAmount, final boolean isInstantPayment) throws PaymentApiException { if (invoice.getBalance().compareTo(BigDecimal.ZERO) <= 0) { throw new PaymentApiException(ErrorCode.PAYMENT_NULL_INVOICE, invoice.getId()); } if (isInstantPayment && inputAmount != null && invoice.getBalance().compareTo(inputAmount) < 0) { throw new PaymentApiException(ErrorCode.PAYMENT_AMOUNT_DENIED, invoice.getId(), inputAmount.floatValue(), invoice.getBalance().floatValue()); } final BigDecimal result = inputAmount != null ? inputAmount : invoice.getBalance(); return result.setScale(2, RoundingMode.HALF_UP); }
@Override public Payment getPayment(final UUID paymentId, final boolean withPluginInfo, final TenantContext context) throws PaymentApiException { final Payment payment = paymentProcessor.getPayment(paymentId, withPluginInfo, internalCallContextFactory.createInternalTenantContext(context)); if (payment == null) { throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT, paymentId); } return payment; }
/** * Compute the refund amount (computed from the invoice or invoice items as necessary). * * @param paymentId payment id associated with this refund * @param specifiedRefundAmount amount to refund. If null, the amount will be the sum of adjusted invoice items * @param invoiceItemIdsWithAmounts invoice item ids and associated amounts to adjust * @return the refund amount */ private BigDecimal computeRefundAmount(final UUID paymentId, @Nullable final BigDecimal specifiedRefundAmount, final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts, final InternalTenantContext context) throws PaymentApiException { try { final List<InvoiceItem> items = invoiceApi.getInvoiceForPaymentId(paymentId, context).getInvoiceItems(); BigDecimal amountFromItems = BigDecimal.ZERO; for (final UUID itemId : invoiceItemIdsWithAmounts.keySet()) { amountFromItems = amountFromItems.add(Objects.firstNonNull(invoiceItemIdsWithAmounts.get(itemId), getAmountFromItem(items, itemId))); } // Sanity check: if some items were specified, then the sum should be equal to specified refund amount, if specified if (amountFromItems.compareTo(BigDecimal.ZERO) != 0 && specifiedRefundAmount != null && specifiedRefundAmount.compareTo(amountFromItems) != 0) { throw new IllegalArgumentException("You can't specify a refund amount that doesn't match the invoice items amounts"); } return Objects.firstNonNull(specifiedRefundAmount, amountFromItems); } catch (InvoiceApiException e) { throw new PaymentApiException(e); } }
protected PaymentPluginApi getPaymentProviderPlugin(final UUID paymentMethodId, final InternalTenantContext context) throws PaymentApiException { final PaymentMethodModelDao methodDao = paymentDao.getPaymentMethodIncludedDeleted(paymentMethodId, context); if (methodDao == null) { log.error("PaymentMethod does not exist", paymentMethodId); throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId); } return getPaymentPluginApi(methodDao.getPluginName()); }
public T processAccountWithLock(final GlobalLocker locker, final String accountExternalKey, final WithAccountLockCallback<T> callback) throws PaymentApiException { GlobalLock lock = null; try { lock = locker.lockWithNumberOfTries(LockerType.ACCOUNT_FOR_INVOICE_PAYMENTS.toString(), accountExternalKey, NB_LOCK_TRY); return callback.doOperation(); } catch (LockFailedException e) { final String format = String.format("Failed to lock account %s", accountExternalKey); log.error(String.format(format), e); throw new PaymentApiException(ErrorCode.PAYMENT_INTERNAL_ERROR, format); } finally { if (lock != null) { lock.release(); } } } }
protected PaymentPluginApi getPaymentProviderPlugin(final Account account, final InternalTenantContext context) throws PaymentApiException { final UUID paymentMethodId = account.getPaymentMethodId(); if (paymentMethodId == null) { throw new PaymentApiException(ErrorCode.PAYMENT_NO_DEFAULT_PAYMENT_METHOD, account.getId()); } return getPaymentProviderPlugin(paymentMethodId, context); }